summaryrefslogtreecommitdiff
path: root/ecos/packages/devs/usb
diff options
context:
space:
mode:
Diffstat (limited to 'ecos/packages/devs/usb')
-rw-r--r--ecos/packages/devs/usb/at91/current/ChangeLog131
-rw-r--r--ecos/packages/devs/usb/at91/current/cdl/usbs_at91.cdl286
-rw-r--r--ecos/packages/devs/usb/at91/current/include/usbs_at91.h82
-rw-r--r--ecos/packages/devs/usb/at91/current/src/bitops.h98
-rw-r--r--ecos/packages/devs/usb/at91/current/src/usbs_at91.c1633
-rw-r--r--ecos/packages/devs/usb/at91/current/src/usbs_at91_data.cxx256
-rw-r--r--ecos/packages/devs/usb/cortexm/stm32/current/ChangeLog41
-rw-r--r--ecos/packages/devs/usb/cortexm/stm32/current/cdl/usb_stm32.cdl325
-rw-r--r--ecos/packages/devs/usb/cortexm/stm32/current/include/usb_stm32.h56
-rw-r--r--ecos/packages/devs/usb/cortexm/stm32/current/src/usb_stm32.c2456
-rw-r--r--ecos/packages/devs/usb/d12/current/ChangeLog27
-rw-r--r--ecos/packages/devs/usb/d12/current/cdl/usbs_d12.cdl318
-rw-r--r--ecos/packages/devs/usb/d12/current/include/usbs_d12.h74
-rw-r--r--ecos/packages/devs/usb/d12/current/src/usbs_d12.c2323
-rw-r--r--ecos/packages/devs/usb/d12/current/src/usbs_d12_data.cxx231
-rw-r--r--ecos/packages/devs/usb/i386/SoRoD12/current/ChangeLog28
-rw-r--r--ecos/packages/devs/usb/i386/SoRoD12/current/cdl/usbs_i386_sorod12.cdl92
-rw-r--r--ecos/packages/devs/usb/i386/SoRoD12/current/include/usbs_i386_sorod12.inl130
-rw-r--r--ecos/packages/devs/usb/nec_upd985xx/current/ChangeLog125
-rw-r--r--ecos/packages/devs/usb/nec_upd985xx/current/cdl/usbs_upd985xx.cdl291
-rw-r--r--ecos/packages/devs/usb/nec_upd985xx/current/doc/devs-usb-nec-upd985xx.html372
-rw-r--r--ecos/packages/devs/usb/nec_upd985xx/current/doc/makefile54
-rw-r--r--ecos/packages/devs/usb/nec_upd985xx/current/doc/usbs_upd985xx.sgml257
-rw-r--r--ecos/packages/devs/usb/nec_upd985xx/current/include/usbs_upd985xx.h78
-rw-r--r--ecos/packages/devs/usb/nec_upd985xx/current/src/usbs_upd985xx.c2687
-rw-r--r--ecos/packages/devs/usb/nec_upd985xx/current/src/usbs_upd985xx_data.cxx189
-rw-r--r--ecos/packages/devs/usb/sa11x0/current/ChangeLog185
-rw-r--r--ecos/packages/devs/usb/sa11x0/current/cdl/usbs_sa11x0.cdl202
-rw-r--r--ecos/packages/devs/usb/sa11x0/current/doc/devs-usb-sa11x0.html341
-rw-r--r--ecos/packages/devs/usb/sa11x0/current/doc/makefile54
-rw-r--r--ecos/packages/devs/usb/sa11x0/current/doc/usbs_sa11x0.sgml234
-rw-r--r--ecos/packages/devs/usb/sa11x0/current/include/usbs_sa11x0.h73
-rw-r--r--ecos/packages/devs/usb/sa11x0/current/src/usbs_sa11x0.c2550
-rw-r--r--ecos/packages/devs/usb/sa11x0/current/src/usbs_sa11x0_data.cxx173
34 files changed, 16452 insertions, 0 deletions
diff --git a/ecos/packages/devs/usb/at91/current/ChangeLog b/ecos/packages/devs/usb/at91/current/ChangeLog
new file mode 100644
index 0000000..0f38086
--- /dev/null
+++ b/ecos/packages/devs/usb/at91/current/ChangeLog
@@ -0,0 +1,131 @@
+2010-11-12 Christophe Coutand <ecos@hotmail.co.uk>
+
+ * src/usbs_at91.c : Complete support for halting endpoints
+ * cdl/usbs_at91.cdl : Add option
+ CYGSEM_DEVS_USB_AT91_ZERO_LENGTH_PACKET_TERMINATION. When data
+ transfer length is known from both side, sending an empty packet
+ at the end of the transfer is not required.
+ Add devtab entry for endpoints 4 to 7.
+
+2008-11-17 Gabor Toeroek <tgabor84@gmail.com>
+
+ * src/usbs_at91.c (usbs_at91_set_pullup): Fix typo introduced by
+ Andrew Lunn in the last patch.
+
+2008-11-03 Gabor Toeroek <tgabor84@gmail.com>
+
+ * cdl/usbs_at91.cdl:
+ * include/usbs_at91.h:
+ * src/usbs_at91.c:
+ * src/usbs_at91_data.c: Support for SAM7SE which has an internal
+ pullup. Added extra endpoints for those chips that have them.
+
+2007-11-20 Andrew Lunn <andrew.lunn@ascom.ch>
+
+ * cdl/usbs_at91.cdl: Fixed typos in
+ CYGNUM_DEVS_USB_AT91_GPIO_PULLUP_INVERTED and
+ CYGNUM_DEVS_USB_AT91_GPIO_READ_POWER_INVERTED. Fixing the typo.
+
+ * src/usbs_at91.c (usbs_at91_set_pullup): Change the logic so that
+ CYGNUM_DEVS_USB_AT91_GPIO_PULLUP_INVERTED does actually cause an
+ invert when set true.
+
+2006-09-07 John Eigelaar <jeigelaar@mweb.co.za>
+
+ * cdl/usbs_at91.c: Read actual EP addresses from the EP configuartion
+ rather than relying on the order in the configuration list.
+
+2006-06-06 Andrew Lunn <andrew.lunn@ascom.ch>
+
+ * cdl/usbs_at91.cdl: Allow EP0 to be enabled when there are no
+ slave clients.
+
+2006-05-25 Andrew Lunn <andrew.lunn@ascom.ch>
+
+ * src/usbs_at91_data.cxx: Change the initialization priority. The
+ USB tty driver is initialized at priority CYG_INIT_IO, so the USB
+ device has to be initialized before that.
+
+2006-05-19 Andrew Lunn <andrew.lunn@ascom.ch>
+
+ * src/usbs_at91.c: Rework pullup and power dectect to use the AT91
+ GPIO macros.
+
+2006-05-07 Andrew Lunn <andrew.lunn@ascom.ch>
+
+ * Included into eCos anonymous CVS.
+
+ Note: There appears to be a hardware bug with OUT transfers. It
+ appears that after resetting the endpoint, the first OUT transfer
+ does not trigger a receive interrupt. The BK0 bit is not set,
+ however the number of bytes in the receiver FIFO is correct. The
+ second OUT transfer causes both BK0 and BK1 bits to be set and an
+ interrupt generated. Currently no workaround is used to correct
+ this behavoiour.
+
+2006-04-23 Andrew Lunn <andrew.lunn@ascom.ch>
+
+ * src/usbs_at91.c: Endpoint 3 can send upto 64 bytes at a time,
+ not 8.
+
+2006-04-20 Andrew Lunn <andrew.lunn@ascom.ch>
+
+ * src/usbs_at91.c (usbs_at91_control_data_sent): Send a zero byte
+ packet when the transfer is an exact multiple of the endpoint
+ buffer size.
+
+2006-04-16 Andrew Lunn <andrew.lunn@ascom.ch>
+
+ * src/usbs_at91.c (usbs_testing_endpoints): Added support for the
+ USB testcase framework by exporting what endpoint we have.
+ * src/usbs_at91.c (usbs_at91_control_setup): Make requests other
+ than standard requests work, eg CLASS, VENDOR etc.
+ * src/usbs_at91.c (usbs_at91_handle_reset): Configure the endpoint
+ hardware using the configuration information.
+ * src/usbs_at91.c (usbs_at91_control_data_sent): Transaction is
+ complete when the buffer is empty _and_ there is no refill
+ function defined.
+
+2006-03-10 Oliver Munz <oli@snr.ch>
+
+ * CDL-Update for PLL-Freuenzy.
+
+2006-03-06 Oliver Munz <oli@snr.ch>
+
+ * USB device driver work finished - haha.
+
+ What's missing:
+ The set_... and get_freature-host-commands are not implemented yet.
+ The dynamical configuration of the data-endpoints is not reflectet in
+ the "usbs.h" API.
+ Isochronus transfer is not implemented.
+ Polling routines are not implemented.
+ USB-Tests are not done.
+ The PowerDetectPin is not used for the state.
+ AT91SAM7X's are not supported at the moment.
+
+ But it works for me...
+
+//===========================================================================
+// ####GPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002, 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####
+//===========================================================================
diff --git a/ecos/packages/devs/usb/at91/current/cdl/usbs_at91.cdl b/ecos/packages/devs/usb/at91/current/cdl/usbs_at91.cdl
new file mode 100644
index 0000000..91c3994
--- /dev/null
+++ b/ecos/packages/devs/usb/at91/current/cdl/usbs_at91.cdl
@@ -0,0 +1,286 @@
+# ====================================================================
+#
+# usbs_at91.cdl
+#
+# USB device driver for the ATMEL AT91 family of processors.
+#
+# ====================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2006, 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): Oliver Munz, Andrew Lunn
+# Original data: bartv
+# Contributors: ccoutand
+# Date: 2006-02-25
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_USB_AT91 {
+ display "Atmel AT91 USB Device Driver"
+ include_dir "cyg/io/usb"
+ parent CYGPKG_IO_USB
+ implements CYGHWR_IO_USB_SLAVE
+
+ cdl_interface CYGINT_DEVS_USB_AT91_HAS_USB {
+ description "
+ This interface is implemented by HALs for devices which have
+ the USB hardware."
+ }
+
+ description "
+ This package provides a suitable eCos device driver
+ for AT91 USB.
+ In this version the driver will support the AT91SAM7S.
+ Other AT92 devices may work, but have not been tested.
+ The Driver needs 48, 96 or 192MHz plus minus 0.25%.
+ Buffers are allocated only in the higher level. There
+ is no need to configure the endpoints in this CDL, because
+ they will be configured dynamical at the set_configuration
+ call from the host...
+ The endpoints 1..3 can be configured as bulk or interrupt
+ IN or OUT endpoint. Isochronous transfer is not supported."
+
+ cdl_component CYGFUN_DEVS_USB_AT91_EP0 {
+ display "Support the control endpoint 0"
+ flavor bool
+ default_value CYGINT_IO_USB_SLAVE_CLIENTS
+ requires CYGPKG_IO_USB CYGPKG_IO_USB_SLAVE
+ requires {
+ ((CYGNUM_HAL_ARM_AT91_CLOCK_SPEED < 48120000 &&
+ CYGNUM_HAL_ARM_AT91_CLOCK_SPEED > 47880000) ||
+ (CYGNUM_HAL_ARM_AT91_CLOCK_SPEED < 96240000 &&
+ CYGNUM_HAL_ARM_AT91_CLOCK_SPEED > 95760000))
+ }
+
+ active_if CYGINT_DEVS_USB_AT91_HAS_USB
+ implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+ implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+
+ compile usbs_at91.c
+ compile -library=libextras.a usbs_at91_data.cxx
+ description "
+ Enable support for endpoint 0. If this support is disabled
+ then the entire USB port is unusable."
+
+ cdl_option CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_INTERNAL {
+ display "The chip has internal pullup"
+ flavor bool
+ calculated { CYGHWR_HAL_ARM_AT91SAM7SE }
+ requires { CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN == "NONE" }
+ description "
+ The chip has an internal pullup resistor;
+ the use of this pullup is mandatory (?), so there
+ should be no external pullup resistor."
+ }
+
+ cdl_option CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN {
+ display "PIO-Pin who controls the pullup resistor"
+ flavor data
+ default_value { "AT91_GPIO_PA16" }
+ description "
+ Every GPIO pin is able to do it. If you don't need
+ a pin because your HW has the pullup fixed wired
+ then select NONE"
+ }
+
+ cdl_option CYGNUM_DEVS_USB_AT91_GPIO_SET_PULLUP_INVERTED {
+ display "Has the signal to be inverted?"
+ active_if {CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN != "NONE"}
+ flavor bool
+ default_value 1
+ description "
+ This option indicates that the pullup pin should
+ be inverted. ie VDD is active, VCC is inactive. For the
+ AT91SAM7SEK it needs to be inverted, hence this default."
+ }
+
+ cdl_option CYGDAT_DEVS_USB_AT91_GPIO_READ_POWER_PIN {
+ display "PIO-Pin who see the USB-Power"
+ flavor data
+ default_value { "AT91_GPIO_PA13"}
+ description "
+ Every GPIO pin is able to do it. If you don't need
+ a pin then select NONE"
+ }
+
+ cdl_option CYGNUM_DEVS_USB_AT91_GPIO_READ_POWER_INVERTED {
+ display "Has the signal to be inverted?"
+ flavor bool
+ default_value 0
+ description "
+ This option indicates that the power detect pin should
+ be inverted. ie VDD is active, VCC is inactive."
+ }
+
+ cdl_option CYGSEM_DEVS_USB_AT91_ZERO_LENGTH_PACKET_TERMINATION {
+ display "Allow USB driver to end data transfer with an empty data packet."
+ flavor bool
+ default_value 1
+ description "
+ This option allows the USB driver to automatically signal the
+ end of a data transmission with an empty packet, if the last packet
+ fragment is equal to the endpoint buffer size."
+ }
+
+ }
+
+ cdl_component CYGPKG_DEVS_USB_AT91_DEVTAB_ENTRIES {
+ display "Provide a devtab entry for endpoints"
+ active_if CYGFUN_DEVS_USB_AT91_EP0
+ default_value 0
+ description "
+ This component controls if /dev/usb entries will be created."
+
+ cdl_option CYGVAR_DEVS_USB_AT91_EP0_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 0"
+ flavor bool
+ default_value 0
+ requires CYGPKG_IO
+ description "
+ If endpoint 0 will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and ioctl calls then a devtab entry is needed."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_AT91_EP1_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 1"
+ flavor bool
+ default_value 1
+ requires CYGPKG_IO
+ description "
+ If this endpoint will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and read calls then a devtab entry is needed."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_AT91_EP2_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 2"
+ flavor bool
+ default_value 1
+ requires CYGPKG_IO
+ description "
+ If this endpoint will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and read calls then a devtab entry is needed."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_AT91_EP3_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 3"
+ flavor bool
+ default_value 1
+ requires CYGPKG_IO
+ description "
+ If this endpoint will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and read calls then a devtab entry is needed."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_AT91_EP4_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 4"
+ flavor bool
+ default_value 0
+ requires CYGPKG_IO
+ description "
+ If this endpoint will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and read calls then a devtab entry is needed."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_AT91_EP5_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 5"
+ flavor bool
+ default_value 0
+ requires CYGPKG_IO
+ description "
+ If this endpoint will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and read calls then a devtab entry is needed."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_AT91_EP6_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 6"
+ flavor bool
+ default_value 0
+ requires CYGPKG_IO
+ description "
+ If this endpoint will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and read calls then a devtab entry is needed."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_AT91_EP7_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 7"
+ flavor bool
+ default_value 0
+ requires CYGPKG_IO
+ description "
+ If this endpoint will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and read calls then a devtab entry is needed."
+ }
+
+ cdl_option CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME {
+ display "Base name for devtab entries"
+ flavor data
+ default_value { "\"/dev/usbs\"" }
+ description "
+ If the uAT91 USB device driver package provides devtab
+ entries for any of the endpoints then this option gives
+ control over the names of these entries. By default the
+ endpoints will be called \"/dev/usbs0c\", \"/dev/usbs3w\"
+ and \"/dev/usbs4r\" (assuming all three endpoints are
+ enabled. The common part \"/dev/usbs\" is determined
+ by this configuration option. It may be necessary to
+ change this if there are multiple USB slave-side
+ devices on the target hardware to prevent a name clash."
+ }
+ }
+}
diff --git a/ecos/packages/devs/usb/at91/current/include/usbs_at91.h b/ecos/packages/devs/usb/at91/current/include/usbs_at91.h
new file mode 100644
index 0000000..f3316ce
--- /dev/null
+++ b/ecos/packages/devs/usb/at91/current/include/usbs_at91.h
@@ -0,0 +1,82 @@
+#ifndef CYGONCE_USBS_AT91_H
+#define CYGONCE_USBS_AT91_H
+//==========================================================================
+//
+// include/usbs_at91.h
+//
+// The interface exported by the AT91 USB device driver
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2006, 2007 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): Oliver Munz
+// Contributors: bartv
+// Date: 2006-02-25
+// Purpose:
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/io/usb/usbs.h>
+#include <pkgconf/devs_usb_at91.h>
+#include <pkgconf/system.h>
+#ifdef CYGPKG_HAL_ARM_AT91SAM7
+#include <pkgconf/hal_arm_at91sam7.h>
+#endif
+
+#if defined(CYGHWR_HAL_ARM_AT91SAM7SE)
+#define AT91_USB_ENDPOINTS 8
+#elif defined(CYGHWR_HAL_ARM_AT91SAM7X)
+#define AT91_USB_ENDPOINTS 6
+#else
+#define AT91_USB_ENDPOINTS 4
+#endif
+
+extern usbs_control_endpoint usbs_at91_ep0;
+extern usbs_rx_endpoint usbs_at91_ep1;
+extern usbs_rx_endpoint usbs_at91_ep2;
+extern usbs_rx_endpoint usbs_at91_ep3;
+#if (AT91_USB_ENDPOINTS > 4)
+extern usbs_rx_endpoint usbs_at91_ep4;
+extern usbs_rx_endpoint usbs_at91_ep5;
+#if (AT91_USB_ENDPOINTS > 6)
+extern usbs_rx_endpoint usbs_at91_ep6;
+extern usbs_rx_endpoint usbs_at91_ep7;
+#endif
+#endif
+
+extern void usbs_at91_endpoint_init(usbs_rx_endpoint * pep,
+ cyg_uint8 endpoint_type, cyg_bool enable);
+#endif /* CYGONCE_USBS_AT91_H */
diff --git a/ecos/packages/devs/usb/at91/current/src/bitops.h b/ecos/packages/devs/usb/at91/current/src/bitops.h
new file mode 100644
index 0000000..78ed1eb
--- /dev/null
+++ b/ecos/packages/devs/usb/at91/current/src/bitops.h
@@ -0,0 +1,98 @@
+#ifndef CYGONCE_USBS_AT91_BITOPS_H
+#define CYGONCE_USBS_AT91_BITOPS_H
+
+//==========================================================================
+//
+// bitops.h
+//
+// Hardware Bit manipulation macros for the AT91 USB device
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 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): Oliver Munz
+// Contributors: bartv
+// Date: 2006-02-22
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+// Set the given bits in a device register
+#define SET_BITS(_register_, _bits_) \
+ CYG_MACRO_START \
+ cyg_uint32 _value_; \
+ HAL_READ_UINT32(_register_, _value_); \
+ _value_ |= _bits_; \
+ HAL_WRITE_UINT32(_register_, _value_); \
+ CYG_MACRO_END
+
+// Clear the given bits in a device register
+#define CLEAR_BITS(_register_, _bits_) \
+ CYG_MACRO_START \
+ cyg_uint32 _value_; \
+ HAL_READ_UINT32(_register_, _value_); \
+ _value_ &= ~_bits_; \
+ HAL_WRITE_UINT32(_register_, _value_); \
+ CYG_MACRO_END
+
+#define BITS_ARE_SET(_register_, _bits) \
+ bits_are_set(_register_, _bits)
+
+#define BITS_ARE_CLEARED(_register_, _bits) \
+ bits_are_cleared(_register_, _bits)
+
+static inline cyg_bool
+bits_are_set (cyg_addrword_t addr, cyg_uint32 bits)
+{
+ cyg_uint32 read;
+
+ HAL_READ_UINT32 (addr, read);
+
+ return (read & bits) == bits;
+}
+
+static inline cyg_bool
+bits_are_cleared (cyg_addrword_t addr, cyg_uint32 bits)
+{
+ cyg_uint32 read;
+
+ HAL_READ_UINT32 (addr, read);
+
+ return (read | ~bits) == ~bits;
+}
+
+
+#endif // CYGONCE_USBS_AT91_BITOPS_H
+
diff --git a/ecos/packages/devs/usb/at91/current/src/usbs_at91.c b/ecos/packages/devs/usb/at91/current/src/usbs_at91.c
new file mode 100644
index 0000000..03db043
--- /dev/null
+++ b/ecos/packages/devs/usb/at91/current/src/usbs_at91.c
@@ -0,0 +1,1633 @@
+//==========================================================================
+//
+// usbs_at91.c
+//
+// Driver for the AT91 USB device
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2006, 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): Oliver Munz,
+// Contributors: Andrew Lunn, bartv, ccoutand
+// Date: 2006-02-22
+//
+// This code implements support for the on-chip USB port on the AT91
+// family of processors. The code has been developed on the AT91SAM7S
+// and may or may not work on other members of the AT91 family.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <pkgconf/system.h>
+#include <pkgconf/devs_usb_at91.h>
+#include <cyg/io/usb/usb.h>
+#include <cyg/io/usb/usbs.h>
+#include <cyg/io/usb/usbs_at91.h>
+
+#include CYGBLD_HAL_PLATFORM_H
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_platform_ints.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/diag.h>
+
+#include "bitops.h"
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define AT91_UDP_CSR0 (AT91_UDP_CSR)
+#define AT91_UDP_FDR0 (AT91_UDP_FDR)
+
+#define pIER (AT91_UDP + AT91_UDP_IER)
+#define pIDR (AT91_UDP + AT91_UDP_IDR)
+#define pISR (AT91_UDP + AT91_UDP_ISR)
+#define pIMR (AT91_UDP + AT91_UDP_IMR)
+#define pICR (AT91_UDP + AT91_UDP_ICR)
+
+#define pCSR0 (AT91_UDP + AT91_UDP_CSR0)
+#define pFDR0 (AT91_UDP + AT91_UDP_FDR0)
+
+#define pCSRn(N) (pCSR0 + (N * 4))
+#define pFDRn(N) (pFDR0 + (N * 4))
+
+#if (AT91_USB_ENDPOINTS == 8)
+#define AT91_UDP_ALLOWED_IRQs \
+ (AT91_UDP_WAKEUP | AT91_UDP_ENDBUSRES | AT91_UDP_EXTRSM | \
+ AT91_UDP_RXRSM | AT91_UDP_RXSUSP | AT91_UDP_EPINT0 | \
+ AT91_UDP_EPINT1 | AT91_UDP_EPINT2 | AT91_UDP_EPINT3 | \
+ AT91_UDP_EPINT4 | AT91_UDP_EPINT5 | \
+ AT91_UDP_EPINT6 | AT91_UDP_EPINT7 )
+#elif (AT91_USB_ENDPOINTS == 6)
+#define AT91_UDP_ALLOWED_IRQs \
+ (AT91_UDP_WAKEUP | AT91_UDP_ENDBUSRES | AT91_UDP_EXTRSM | \
+ AT91_UDP_RXRSM | AT91_UDP_RXSUSP | AT91_UDP_EPINT0 | \
+ AT91_UDP_EPINT1 | AT91_UDP_EPINT2 | AT91_UDP_EPINT3 | \
+ AT91_UDP_EPINT4 | AT91_UDP_EPINT5 )
+#elif (AT91_USB_ENDPOINTS == 4)
+#define AT91_UDP_ALLOWED_IRQs \
+ (AT91_UDP_WAKEUP | AT91_UDP_ENDBUSRES | AT91_UDP_EXTRSM | \
+ AT91_UDP_RXRSM | AT91_UDP_RXSUSP | AT91_UDP_EPINT0 | \
+ AT91_UDP_EPINT1 | AT91_UDP_EPINT2 | AT91_UDP_EPINT3)
+#endif
+
+#define THERE_IS_A_NEW_PACKET_IN_THE_UDP 0xffff
+
+// Fifo size for each end point.
+#if defined(CYGHWR_HAL_ARM_AT91SAM7SE)
+static const cyg_uint16 usbs_at91_endpoint_fifo_size[AT91_USB_ENDPOINTS] = {
+ 8,
+ 64,
+ 64,
+ 64,
+ 512,
+ 512,
+ 64,
+ 64
+};
+#elif defined(CYGHWR_HAL_ARM_AT91SAM7X)
+static const cyg_uint16 usbs_at91_endpoint_fifo_size[AT91_USB_ENDPOINTS] = {
+ 8,
+ 64,
+ 64,
+ 64,
+ 256,
+ 256
+};
+#else
+static const cyg_uint16 usbs_at91_endpoint_fifo_size[AT91_USB_ENDPOINTS] = {
+ 8,
+ 64,
+ 64,
+ 64,
+};
+#endif
+
+// Does an endpoint support ping pong buffering?
+#if defined(CYGHWR_HAL_ARM_AT91SAM7SE)
+static const bool usbs_at91_endpoint_pingpong[AT91_USB_ENDPOINTS] = {
+ false,
+ true,
+ true,
+ false,
+ true,
+ true,
+ true,
+ true
+};
+#elif defined(CYGHWR_HAL_ARM_AT91SAM7X)
+static const bool usbs_at91_endpoint_pingpong[AT91_USB_ENDPOINTS] = {
+ false,
+ true,
+ true,
+ false,
+ true,
+ true
+};
+#else
+static const bool usbs_at91_endpoint_pingpong[AT91_USB_ENDPOINTS] = {
+ false,
+ true,
+ true,
+ false
+};
+#endif
+
+static cyg_uint8 *usbs_at91_endpoint_pbegin[AT91_USB_ENDPOINTS] =
+#if (AT91_USB_ENDPOINTS == 8)
+ { 0, 0, 0, 0, 0, 0, 0, 0 };
+#elif (AT91_USB_ENDPOINTS == 6)
+ { 0, 0, 0, 0, 0, 0 };
+#else
+ { 0, 0, 0, 0 };
+#endif
+
+static cyg_uint8 *usbs_at91_endpoint_pend[AT91_USB_ENDPOINTS] =
+#if (AT91_USB_ENDPOINTS == 8)
+ { 0, 0, 0, 0, 0, 0, 0, 0 };
+#elif (AT91_USB_ENDPOINTS == 6)
+ { 0, 0, 0, 0, 0, 0 };
+#else
+ { 0, 0 ,0, 0 };
+#endif
+
+static bool usbs_at91_endpoint_bank1[AT91_USB_ENDPOINTS] =
+#if (AT91_USB_ENDPOINTS == 8)
+ { false, false, false, false, false, false, false, false };
+#elif (AT91_USB_ENDPOINTS == 6)
+ { false, false, false, false, false, false };
+#else
+ { false, false, false, false };
+#endif
+
+static cyg_uint16 usbs_at91_endpoint_bytes_in_fifo[AT91_USB_ENDPOINTS] =
+#if (AT91_USB_ENDPOINTS == 8)
+ { 0, 0, 0, 0, 0, 0, 0, 0 };
+#elif (AT91_USB_ENDPOINTS == 6)
+ { 0, 0, 0, 0, 0, 0 };
+#else
+ { 0, 0, 0, 0 };
+#endif
+
+static cyg_uint16 usbs_at91_endpoint_bytes_received[AT91_USB_ENDPOINTS] =
+ { THERE_IS_A_NEW_PACKET_IN_THE_UDP, THERE_IS_A_NEW_PACKET_IN_THE_UDP,
+#if (AT91_USB_ENDPOINTS > 4)
+ THERE_IS_A_NEW_PACKET_IN_THE_UDP, THERE_IS_A_NEW_PACKET_IN_THE_UDP,
+#if (AT91_USB_ENDPOINTS > 6)
+ THERE_IS_A_NEW_PACKET_IN_THE_UDP, THERE_IS_A_NEW_PACKET_IN_THE_UDP,
+#endif
+#endif
+ THERE_IS_A_NEW_PACKET_IN_THE_UDP, THERE_IS_A_NEW_PACKET_IN_THE_UDP};
+
+static cyg_interrupt usbs_at91_intr_data;
+static cyg_handle_t usbs_at91_intr_handle;
+
+static void usbs_at91_ep0_start(usbs_control_endpoint *);
+static void usbs_at91_poll(usbs_control_endpoint *);
+
+static void usbs_at91_endpoint_start(usbs_rx_endpoint * pep);
+static void usbs_at91_endpoint_set_halted(usbs_rx_endpoint * pep,
+ cyg_bool new_value);
+void usbs_at91_endpoint_init(usbs_rx_endpoint * pep,
+ cyg_uint8 endpoint_type,
+ cyg_bool enable);
+
+// Endpoint 0, the control endpoint, structure.
+usbs_control_endpoint usbs_at91_ep0 = {
+ // The hardware does not distinguish between detached, attached and powered.
+ state: USBS_STATE_POWERED,
+ enumeration_data: (usbs_enumeration_data *) 0,
+ start_fn: usbs_at91_ep0_start,
+ poll_fn: usbs_at91_poll,
+ interrupt_vector: CYGNUM_HAL_INTERRUPT_UDP,
+ control_buffer: {0, 0, 0, 0, 0, 0, 0, 0},
+ state_change_fn: (void (*) (usbs_control_endpoint *,
+ void *, usbs_state_change, int)) 0,
+ state_change_data: (void *) 0,
+ standard_control_fn: (usbs_control_return (*)
+ (usbs_control_endpoint *, void *)) 0,
+ standard_control_data: (void *) 0,
+ class_control_fn: (usbs_control_return (*)
+ (usbs_control_endpoint *, void *)) 0,
+ class_control_data: (void *) 0,
+ vendor_control_fn: (usbs_control_return (*)
+ (usbs_control_endpoint *, void *)) 0,
+ vendor_control_data: (void *) 0,
+ reserved_control_fn: (usbs_control_return (*)
+ (usbs_control_endpoint *, void *)) 0,
+ reserved_control_data: (void *) 0,
+ buffer: (unsigned char *) 0,
+ buffer_size: 0,
+ fill_buffer_fn: (void (*)(usbs_control_endpoint *)) 0,
+ fill_data: (void *) 0,
+ fill_index: 0,
+ complete_fn: (usbs_control_return (*)(usbs_control_endpoint *,
+ int)) 0
+};
+
+// Endpoint 1 receive control structure
+usbs_rx_endpoint usbs_at91_ep1 = {
+ start_rx_fn: usbs_at91_endpoint_start,
+ set_halted_fn: usbs_at91_endpoint_set_halted,
+ complete_fn: (void (*)(void *, int)) 0,
+ complete_data: (void *) 0,
+ buffer: (unsigned char *) 0,
+ buffer_size: 0,
+ halted: 0,
+};
+
+// Endpoint 2 Receive control structure
+usbs_rx_endpoint usbs_at91_ep2 = {
+ start_rx_fn: usbs_at91_endpoint_start,
+ set_halted_fn: usbs_at91_endpoint_set_halted,
+ complete_fn: (void (*)(void *, int)) 0,
+ complete_data: (void *) 0,
+ buffer: (unsigned char *) 0,
+ buffer_size: 0,
+ halted: 0,
+};
+
+// Endpoint 3 Receive control structure
+usbs_rx_endpoint usbs_at91_ep3 = {
+ start_rx_fn: usbs_at91_endpoint_start,
+ set_halted_fn: usbs_at91_endpoint_set_halted,
+ complete_fn: (void (*)(void *, int)) 0,
+ complete_data: (void *) 0,
+ buffer: (unsigned char *) 0,
+ buffer_size: 0,
+ halted: 0,
+};
+
+#if (AT91_USB_ENDPOINTS > 4)
+// Endpoint 4 Receive control structure
+usbs_rx_endpoint usbs_at91_ep4 = {
+ start_rx_fn: usbs_at91_endpoint_start,
+ set_halted_fn: usbs_at91_endpoint_set_halted,
+ complete_fn: (void (*)(void *, int)) 0,
+ complete_data: (void *) 0,
+ buffer: (unsigned char *) 0,
+ buffer_size: 0,
+ halted: 0,
+};
+#endif
+
+#if (AT91_USB_ENDPOINTS > 5)
+// Endpoint 5 Receive control structure
+usbs_rx_endpoint usbs_at91_ep5 = {
+ start_rx_fn: usbs_at91_endpoint_start,
+ set_halted_fn: usbs_at91_endpoint_set_halted,
+ complete_fn: (void (*)(void *, int)) 0,
+ complete_data: (void *) 0,
+ buffer: (unsigned char *) 0,
+ buffer_size: 0,
+ halted: 0,
+};
+#endif
+
+#if (AT91_USB_ENDPOINTS > 6)
+// Endpoint 6 Receive control structure
+usbs_rx_endpoint usbs_at91_ep6 = {
+ start_rx_fn: usbs_at91_endpoint_start,
+ set_halted_fn: usbs_at91_endpoint_set_halted,
+ complete_fn: (void (*)(void *, int)) 0,
+ complete_data: (void *) 0,
+ buffer: (unsigned char *) 0,
+ buffer_size: 0,
+ halted: 0,
+};
+#endif
+
+#if (AT91_USB_ENDPOINTS > 7)
+// Endpoint 7 Receive control structure
+usbs_rx_endpoint usbs_at91_ep7 = {
+ start_rx_fn: usbs_at91_endpoint_start,
+ set_halted_fn: usbs_at91_endpoint_set_halted,
+ complete_fn: (void (*)(void *, int)) 0,
+ complete_data: (void *) 0,
+ buffer: (unsigned char *) 0,
+ buffer_size: 0,
+ halted: 0,
+};
+#endif
+
+// Array of end points. Used for translating end point pointer to an
+// end point number
+static const void *usbs_at91_endpoints[AT91_USB_ENDPOINTS] = {
+ (void *) &usbs_at91_ep0,
+ (void *) &usbs_at91_ep1,
+ (void *) &usbs_at91_ep2,
+ (void *) &usbs_at91_ep3
+#if (AT91_USB_ENDPOINTS > 4)
+ ,(void *) &usbs_at91_ep4, (void *) &usbs_at91_ep5
+#if (AT91_USB_ENDPOINTS > 6)
+ ,(void *) &usbs_at91_ep6, (void *) &usbs_at91_ep7
+#endif
+#endif
+};
+
+// Convert an endpoint pointer to an endpoint number, using the array
+// of endpoint structures
+static int
+usbs_at91_pep_to_number(const usbs_rx_endpoint * pep)
+{
+ int epn;
+
+ for(epn=0; epn < AT91_USB_ENDPOINTS; epn++) {
+ if (pep == usbs_at91_endpoints[epn])
+ return epn;
+ }
+ CYG_FAIL("Unknown endpoint");
+ return 0;
+}
+
+typedef enum ep0_low_level_status_t {
+ EP0_LL_IDLE = 0,
+ EP0_LL_REQUEST,
+ EP0_LL_SEND_READY,
+ EP0_LL_ACK,
+ EP0_LL_RECEIVE_READY,
+ EP0_LL_ISOERROR,
+ EP0_LL_STALL,
+ EP0_LL_SET_ADDRESS,
+} ep0_low_level_status_t;
+
+// Enable/Disable interrupts for a specific endpoint.
+static void
+usbs_at91_endpoint_interrupt_enable (cyg_uint8 epn, bool enable)
+{
+ CYG_ASSERT (epn < AT91_USB_ENDPOINTS, "Invalid endpoint");
+
+ if (enable) {
+ HAL_WRITE_UINT32 (pIER, 1 << epn);
+ } else {
+ HAL_WRITE_UINT32 (pIDR, 1 << epn);
+ }
+}
+
+static cyg_uint8 *
+read_fifo_uint8 (cyg_uint8 * pdest, cyg_addrword_t psource, cyg_uint32 size)
+{
+ cyg_uint8 *preqbyte = pdest;
+ cyg_uint8 reqbyte;
+
+ while (size--) {
+ HAL_READ_UINT8 (psource, reqbyte);
+ *preqbyte = reqbyte;
+ preqbyte++;
+ }
+
+ return preqbyte;
+}
+
+static cyg_uint8 *
+write_fifo_uint8 (cyg_addrword_t pdest, cyg_uint8 * psource,
+ cyg_uint8 * psource_end)
+{
+ cyg_uint8 *preqbyte;
+
+ for (preqbyte = psource; preqbyte < psource_end; preqbyte++) {
+ HAL_WRITE_UINT8 (pdest, (*preqbyte));
+ }
+
+ return preqbyte;
+}
+
+/* Tell the host that the device is ready to start communication */
+static void
+usbs_at91_set_pullup (bool set)
+{
+#ifdef CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_INTERNAL
+ cyg_uint32 txvc;
+ HAL_READ_UINT32(AT91_UDP + AT91_UDP_TXVC, txvc);
+ if (set) {
+ txvc |= AT91_UDP_TXVC_PUON;
+ } else {
+ txvc &= ~AT91_UDP_TXVC_PUON;
+ }
+ HAL_WRITE_UINT32(AT91_UDP + AT91_UDP_TXVC, txvc);
+#endif // CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_INTERNAL
+
+#ifndef CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN_NONE
+ if (
+#ifdef CYGNUM_DEVS_USB_AT91_GPIO_SET_PULLUP_INVERTED
+ !set
+#else
+ set
+#endif
+ ) {
+ HAL_ARM_AT91_GPIO_SET(CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN);
+ } else {
+ HAL_ARM_AT91_GPIO_RESET(CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN);
+ }
+#endif
+}
+
+/* Is the USB powered? */
+bool
+usbs_at91_read_power (void)
+{
+#ifndef CYGDAT_DEVS_USB_AT91_GPIO_READ_POWER_PIN_NONE
+ cyg_bool state;
+
+ HAL_ARM_AT91_GPIO_GET(CYGDAT_DEVS_USB_AT91_GPIO_READ_POWER_PIN, state);
+#ifdef CYGNUM_DEVS_USB_AT91_GPIO_READ_POWER_INVERTED
+ return !state;
+#else
+ return state;
+#endif
+#endif
+ return true;
+}
+
+// Stop all transfers that are currently active.
+static void
+usbs_end_all_transfers (usbs_control_return returncode)
+{
+ cyg_uint32 epn;
+ usbs_rx_endpoint *pep;
+
+ for (epn = 1; epn < AT91_USB_ENDPOINTS; epn++) {
+ if (BITS_ARE_SET (pIMR, 1 << epn)) {
+ // If the end point is transmitting, call the complete function
+ // to terminate to transfer
+ pep = (usbs_rx_endpoint *) usbs_at91_endpoints[epn];
+
+ if (pep->complete_fn) {
+ (*pep->complete_fn) (pep->complete_data, returncode);
+ }
+ // Disable interrupts from the endpoint
+ usbs_at91_endpoint_interrupt_enable (epn, false);
+ }
+ }
+}
+
+// There has been a change in state. Update the end point.
+static void
+usbs_state_notify (usbs_control_endpoint * pcep)
+{
+ static int old_state = USBS_STATE_CHANGE_POWERED;
+ int state = pcep->state & USBS_STATE_MASK;
+
+ if (pcep->state != old_state) {
+ usbs_end_all_transfers (-EPIPE);
+ switch (state) {
+ case USBS_STATE_DETACHED:
+ case USBS_STATE_ATTACHED:
+ case USBS_STATE_POWERED:
+ // Nothing to do
+ break;
+ case USBS_STATE_DEFAULT:
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_GLB_STATE, 0);
+ break;
+ case USBS_STATE_ADDRESSED:
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_GLB_STATE, AT91_UDP_GLB_FADDEN);
+ break;
+ case USBS_STATE_CONFIGURED:
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_GLB_STATE, AT91_UDP_GLB_CONFG);
+ break;
+ default:
+ CYG_FAIL("Unknown endpoint state");
+ }
+
+ if (pcep->state_change_fn) {
+ (*pcep->state_change_fn) (pcep, pcep->state_change_data, pcep->state, old_state);
+ }
+
+ old_state = pcep->state;
+ }
+}
+
+static usbs_control_return
+usbs_parse_host_get_command (usbs_control_endpoint * pcep)
+{
+ usbs_control_return retcode;
+ cyg_uint8 dev_req_type =
+ (((usb_devreq *) pcep->control_buffer)->type) & USB_DEVREQ_TYPE_MASK;
+
+ switch (dev_req_type) {
+ case USB_DEVREQ_TYPE_STANDARD:
+ if (!pcep->standard_control_fn) {
+ return usbs_handle_standard_control (pcep);
+ }
+
+ retcode =
+ (*pcep->standard_control_fn) (pcep, pcep->standard_control_data);
+
+ if (retcode == USBS_CONTROL_RETURN_UNKNOWN) {
+ return usbs_handle_standard_control (pcep);
+ }
+ return retcode;
+
+ case USB_DEVREQ_TYPE_CLASS:
+ if (!pcep->class_control_fn) {
+ return USBS_CONTROL_RETURN_STALL;
+ }
+ return (*pcep->class_control_fn) (pcep, pcep->class_control_data);
+
+ case USB_DEVREQ_TYPE_VENDOR:
+ if (!pcep->class_control_fn) {
+ return USBS_CONTROL_RETURN_STALL;
+ }
+ return (*pcep->class_control_fn) (pcep, pcep->vendor_control_data);
+
+ case USB_DEVREQ_TYPE_RESERVED:
+ if (!pcep->reserved_control_fn) {
+ return USBS_CONTROL_RETURN_STALL;
+ }
+ return (*pcep->reserved_control_fn) (pcep, pcep->reserved_control_data);
+ default:
+ return USBS_CONTROL_RETURN_STALL;
+ }
+}
+
+static void
+usbs_at91_endpoint_set_halted (usbs_rx_endpoint * pep, cyg_bool new_value)
+{
+ int epn = usbs_at91_pep_to_number(pep);
+ cyg_addrword_t pCSR = pCSRn(epn);
+ cyg_uint32 reg;
+
+ cyg_drv_dsr_lock ();
+
+ if (pep->halted != new_value) {
+ /* There is something is to do */
+ pep->halted = new_value;
+
+ if ( new_value ) {
+
+ /* Halt endpoint */
+ SET_BITS (pCSR, AT91_UDP_CSR_FORCESTALL);
+
+ } else {
+ /* Restart endpoint */
+ CLEAR_BITS (pCSR, AT91_UDP_CSR_FORCESTALL);
+
+ // Reset Fifo
+ HAL_READ_UINT32 (AT91_UDP + AT91_UDP_RST_EP, reg);
+ reg |= (1 << epn);
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, reg);
+ reg &= ~(1 << epn);
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, reg);
+
+ // Ready to use
+ if (pep->complete_fn) {
+ (*pep->complete_fn) (pep->complete_data, ENOERR);
+ }
+
+ }
+ }
+ cyg_drv_dsr_unlock ();
+}
+
+void
+usbs_at91_endpoint_init (usbs_rx_endpoint * pep, cyg_uint8 endpoint_type,
+ cyg_bool enable)
+{
+ int epn = usbs_at91_pep_to_number(pep);
+ cyg_addrword_t pCSR = pCSRn(epn);
+
+ CYG_ASSERT (AT91_USB_ENDPOINTS > epn, "Invalid end point");
+
+ usbs_at91_endpoint_interrupt_enable (epn, false);
+ /* Reset endpoint */
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, 1 << epn);
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, 0);
+
+ pep->halted = false;
+
+ /* Type | In */
+ HAL_WRITE_UINT32 (pCSR, (((((cyg_uint32) endpoint_type) & 0x03) << 8) |
+ ((((cyg_uint32) endpoint_type) & 0x80) << 3)));
+
+ usbs_at91_endpoint_bytes_in_fifo[epn] = 0;
+ usbs_at91_endpoint_bytes_received[epn] = THERE_IS_A_NEW_PACKET_IN_THE_UDP;
+ usbs_at91_endpoint_bank1[epn] = false;
+
+ if (enable) {
+ SET_BITS (pCSR, AT91_UDP_CSR_EPEDS);
+ }
+}
+
+static void
+usbs_at91_reset_device (void)
+{
+ int epn;
+
+ usbs_end_all_transfers (-EPIPE);
+
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_IDR, 0xffffffff);
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_ICR, 0xffffffff);
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, 0xffffffff);
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_RST_EP, 0x00000000);
+
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_FADDR, AT91_UDP_FADDR_FEN);
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_CSR0,
+ AT91_UDP_CSR_EPEDS | AT91_UDP_CSR_EPTYPE_CTRL);
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_IER, AT91_UDP_ALLOWED_IRQs);
+
+ for (epn=1; epn < AT91_USB_ENDPOINTS; epn++) {
+ usbs_at91_endpoint_init ((usbs_rx_endpoint *)usbs_at91_endpoints[epn],
+ 0, false);
+ }
+}
+
+static void
+usbs_at91_handle_reset (void)
+{
+ int epn;
+ const usb_endpoint_descriptor *usb_endpoints;
+ cyg_uint8 endpoint_type;
+
+ cyg_uint8 endpoint_number;
+
+ usbs_at91_reset_device ();
+
+ // Now walk the endpoints configuring them correctly. This only
+ // works if there is one interface.
+ usb_endpoints = usbs_at91_ep0.enumeration_data->endpoints;
+
+ for (epn = 1;
+ epn <= usbs_at91_ep0.enumeration_data->total_number_endpoints;
+ epn++) {
+
+ endpoint_type = (usb_endpoints[epn-1].attributes |
+ (usb_endpoints[epn-1].endpoint &
+ USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN ?
+ USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN :
+ USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT));
+ endpoint_number = usb_endpoints[epn-1].endpoint & ~(USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN);
+
+ if ( endpoint_number < AT91_USB_ENDPOINTS ) {
+ usbs_at91_endpoint_init((usbs_rx_endpoint *)usbs_at91_endpoints[endpoint_number],
+ endpoint_type,
+ true);
+ }
+ }
+}
+
+static void
+usbs_at91_ep0_start (usbs_control_endpoint * endpoint)
+{
+ usbs_at91_handle_reset ();
+
+ // If there is additional platform-specific initialization to
+ // perform, do it now. This macro can come from the platform HAL,
+ // but may not be available on all platforms.
+#ifdef AT91_USB_PLATFORM_INIT
+ AT91_USB_PLATFORM_INIT ();
+#endif
+
+ usbs_at91_set_pullup (true);
+ CLEAR_BITS(AT91_UDP + AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
+}
+
+static void
+usbs_at91_endpoint_start (usbs_rx_endpoint * pep)
+{
+ int epn = usbs_at91_pep_to_number(pep);
+ cyg_addrword_t pCSR = pCSRn(epn);
+ cyg_addrword_t pFDR = pFDRn(epn);
+ cyg_uint16 space = 0;
+ cyg_uint16 endpoint_size = usbs_at91_endpoint_fifo_size[epn];
+ cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[epn];
+ cyg_uint8 **ppend = &usbs_at91_endpoint_pend[epn];
+
+ CYG_ASSERT (pep->complete_fn, "No complete_fn()");
+
+ cyg_drv_dsr_lock ();
+ if (usbs_at91_ep0.state != USBS_STATE_CONFIGURED) {
+ /* If not configured it means there is nothing to do */
+ cyg_drv_dsr_unlock ();
+
+ if (pep->complete_fn) {
+ (*pep->complete_fn) (pep->complete_data, -EPIPE);
+ }
+ return;
+ }
+
+ // Is this endpoint currently stalled? If so then a size of 0 can
+ // be used to block until the stall condition is clear, anything
+ // else should result in an immediate callback.
+ if (pep->halted) {
+ /* Halted means nothing to do */
+ cyg_drv_dsr_unlock ();
+
+ if (pep->complete_fn && pep->buffer_size != 0) {
+ (*pep->complete_fn) (pep->complete_data, -EAGAIN);
+ }
+ return;
+ }
+
+ if (BITS_ARE_SET (pIMR, 1 << epn)) {
+ cyg_drv_dsr_unlock ();
+
+ if (pep->complete_fn) {
+ (*pep->complete_fn) (pep->complete_data, -EIO);
+ }
+
+ return;
+ }
+
+ CYG_ASSERT (BITS_ARE_SET (pCSR, 1 << 9), "Wrong endpoint type");
+
+ *ppbegin = pep->buffer; /* Set the working pointers */
+ *ppend = (cyg_uint8 *) ((cyg_uint32) pep->buffer + pep->buffer_size);
+
+ if (BITS_ARE_SET (pCSR, 0x400)) { /* IN: tx_endpoint */
+ space = (cyg_uint32) * ppend - (cyg_uint32) * ppbegin;
+
+#ifdef CYGSEM_DEVS_USB_AT91_ZERO_LENGTH_PACKET_TERMINATION
+ if (space == endpoint_size) {
+ *ppend = *ppbegin; /* Send zero-packet */
+ }
+#endif
+
+ *ppbegin =
+ write_fifo_uint8 (pFDR, *ppbegin,
+ (cyg_uint8 *) ((cyg_uint32) * ppbegin +
+ MIN (space, endpoint_size)));
+ SET_BITS (pCSR, AT91_UDP_CSR_TXPKTRDY);
+
+ if (*ppend == *ppbegin) { /* Last packet ? */
+ *ppend = *ppbegin - 1; /* The packet hasn't been sent yet */
+ }
+ }
+
+ usbs_at91_endpoint_interrupt_enable (epn, true);
+
+ cyg_drv_dsr_unlock ();
+}
+
+// Perform transmit handling on an endpoint
+static bool
+usbs_at91_endpoint_isr_tx(cyg_uint8 epn)
+{
+ cyg_addrword_t pCSR = pCSRn(epn);
+ cyg_addrword_t pFDR = pFDRn(epn);
+ cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[epn];
+ cyg_uint8 **ppend = &usbs_at91_endpoint_pend[epn];
+ cyg_uint32 space = 0;
+ cyg_uint16 endpoint_size = usbs_at91_endpoint_fifo_size[epn];
+
+ CLEAR_BITS (pCSR, AT91_UDP_CSR_TXCOMP);
+
+ if (BITS_ARE_CLEARED (pCSR, AT91_UDP_CSR_TXPKTRDY)) {
+ /* Ready to transmit ? */
+ if (*ppend > *ppbegin) {
+ /* Something to send */
+
+ space = (cyg_uint32) * ppend - (cyg_uint32) * ppbegin;
+
+#ifdef CYGSEM_DEVS_USB_AT91_ZERO_LENGTH_PACKET_TERMINATION
+ if (space == endpoint_size) {
+ *ppend = *ppbegin; /* Send zero-packet */
+ }
+#endif
+
+ *ppbegin =
+ write_fifo_uint8 (pFDR, *ppbegin,
+ (cyg_uint8 *) ((cyg_uint32) * ppbegin +
+ MIN (space, endpoint_size)));
+ SET_BITS (pCSR, AT91_UDP_CSR_TXPKTRDY);
+
+ if (*ppend == *ppbegin) { /* Last packet ? */
+ *ppend = *ppbegin - 1; /* The packet isn't sent yet */
+ }
+
+ } else {
+
+#ifdef CYGSEM_DEVS_USB_AT91_ZERO_LENGTH_PACKET_TERMINATION
+ if (*ppend + 1 == *ppbegin) {
+ *ppend = *ppbegin; /* Flag for DSR */
+ return true;
+ } else {
+ *ppend = *ppbegin - 1; /* Flag for zero-packet */
+ SET_BITS (pCSR, AT91_UDP_CSR_TXPKTRDY); /* Send no data */
+ }
+#else
+ *ppend = *ppbegin; /* Flag for DSR */
+ return true;
+#endif
+
+ }
+ }
+
+ CLEAR_BITS (pCSR,
+ AT91_UDP_CSR_RX_DATA_BK0 | AT91_UDP_CSR_RX_DATA_BK1 |
+ AT91_UDP_CSR_RXSETUP | AT91_UDP_CSR_ISOERROR);
+ return false;
+}
+
+static bool
+usbs_at91_endpoint_isr_rx(cyg_uint8 epn)
+{
+ cyg_addrword_t pCSR = pCSRn(epn);
+ cyg_addrword_t pFDR = pFDRn(epn);
+ cyg_uint16 *pinfifo = &usbs_at91_endpoint_bytes_in_fifo[epn];
+ cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[epn];
+ cyg_uint8 **ppend = &usbs_at91_endpoint_pend[epn];
+ cyg_uint16 *preceived = &usbs_at91_endpoint_bytes_received[epn];
+ cyg_uint16 endpoint_size = usbs_at91_endpoint_fifo_size[epn];
+
+ if (*preceived == THERE_IS_A_NEW_PACKET_IN_THE_UDP) {
+ /* There is a new packet */
+ *preceived = ((*(cyg_uint32 *)pCSR) >> 16) & 0x7ff;
+ *pinfifo = *preceived;
+ }
+
+ while ((*ppbegin < *ppend) && *pinfifo) {
+ /* If we have buffer-space AND data in the FIFO */
+ HAL_READ_UINT8(pFDR, **ppbegin);
+ (*ppbegin)++;
+ (*pinfifo)--;
+ }
+
+ if (*ppbegin == *ppend) {
+ /* The buffer is full... call the DSR */
+ return true;
+ }
+
+ if (*pinfifo == 0) {
+ /* If the FIFO is empty, then we can release it */
+ if (usbs_at91_endpoint_pingpong[epn]) {
+ /* Time to clear the interrupt flag */
+
+ if (usbs_at91_endpoint_bank1[epn]) {
+ CLEAR_BITS (pCSR, AT91_UDP_CSR_RX_DATA_BK1);
+ } else {
+ CLEAR_BITS (pCSR, AT91_UDP_CSR_RX_DATA_BK0);
+ }
+ usbs_at91_endpoint_bank1[epn] = !usbs_at91_endpoint_bank1[epn];
+ } else {
+ CLEAR_BITS (pCSR, AT91_UDP_CSR_RX_DATA_BK0);
+ }
+
+ if (*preceived < endpoint_size) {
+ /* If the last packet was smaller then the endpoint-size... */
+ *ppend = *ppbegin;
+ *preceived = THERE_IS_A_NEW_PACKET_IN_THE_UDP; /* Set flag */
+
+ return true; /* We can call the completion-function in the DSR */
+ }
+
+ *preceived = THERE_IS_A_NEW_PACKET_IN_THE_UDP; /* Set flag */
+ }
+ return false;
+}
+
+// ISR for an endpoint. Handle receive and transmit interrupts.
+static bool
+usbs_at91_endpoint_isr (cyg_uint8 epn)
+{
+ cyg_addrword_t pCSR = pCSRn(epn);
+
+ CYG_ASSERT (AT91_USB_ENDPOINTS > epn && epn, "Invalid end point");
+
+ // Host has acknowledged the endpoint being stall
+ if (BITS_ARE_SET (pCSR, AT91_UDP_CSR_ISOERROR)) {
+ CLEAR_BITS (pCSR, AT91_UDP_CSR_ISOERROR);
+ }
+
+ if (BITS_ARE_SET (pCSR, 0x400)) { /* IN: tx_endpoint */
+ if (usbs_at91_endpoint_isr_tx(epn))
+ return true; // Call the DSR
+ } else { /* OUT: rx_endpoint */
+ if (!BITS_ARE_CLEARED (pCSR, AT91_UDP_CSR_RX_DATA_BK0) ||
+ !BITS_ARE_CLEARED (pCSR, AT91_UDP_CSR_RX_DATA_BK1)) {
+ /* Sometime something received ? */
+ if(usbs_at91_endpoint_isr_rx(epn))
+ return true; // Call the DSR;
+ }
+
+ CLEAR_BITS (pCSR,
+ AT91_UDP_CSR_TXCOMP | AT91_UDP_CSR_RXSETUP |
+ AT91_UDP_CSR_ISOERROR);
+ }
+
+ return false;
+}
+
+// Handle a DSR for an endpoint
+static void
+usbs_at91_endpoint_dsr (cyg_uint8 epn)
+{
+ usbs_rx_endpoint *pep = (usbs_rx_endpoint *) usbs_at91_endpoints[epn];
+ cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[epn];
+ cyg_uint8 **ppend = &usbs_at91_endpoint_pend[epn];
+
+ CYG_ASSERT (AT91_USB_ENDPOINTS > epn && epn, "Invalid end point");
+ CYG_ASSERT (pep->complete_fn, "No complete_fn()");
+
+ if (*ppend == *ppbegin) { /* Transmitted/Received ? */
+
+ pep->buffer_size = (cyg_uint32) * ppbegin - (cyg_uint32) pep->buffer;
+ if (pep->complete_fn)
+ {
+ /* Do not check on pep->buffer_size != 0, user should
+ * be allowed to send empty packet */
+ if (!pep->halted) {
+ (*pep->complete_fn) (pep->complete_data, pep->buffer_size);
+ } else {
+ (*pep->complete_fn) (pep->complete_data, -EAGAIN);
+ }
+ }
+ usbs_at91_endpoint_interrupt_enable (epn, false);
+ }
+}
+
+// Handle an error condition on the control endpoint
+static ep0_low_level_status_t
+usbs_at91_control_error(ep0_low_level_status_t status)
+{
+ cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0];
+ cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0];
+
+ usbs_at91_ep0.buffer_size = 0;
+ usbs_at91_ep0.fill_buffer_fn = 0;
+ usbs_at91_ep0.complete_fn = 0;
+
+ *ppbegin = usbs_at91_ep0.buffer;
+ *ppend = *ppbegin;
+
+ if (status == EP0_LL_IDLE) {
+ if (usbs_at91_ep0.complete_fn) {
+ (*usbs_at91_ep0.complete_fn) (&usbs_at91_ep0,
+ USBS_CONTROL_RETURN_STALL);
+ }
+ }
+
+ status = EP0_LL_IDLE;
+
+ CLEAR_BITS (pCSR0, AT91_UDP_CSR_ISOERROR | AT91_UDP_CSR_FORCESTALL);
+
+ return status;
+}
+
+// Handle a get status setup message on the control end point
+static ep0_low_level_status_t
+usbs_at91_control_setup_get_status(void)
+{
+ ep0_low_level_status_t status;
+ usb_devreq *req = (usb_devreq *)usbs_at91_ep0.control_buffer;
+ cyg_uint8 recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
+ cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0];
+ cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0];
+ cyg_uint16 word = 0;
+
+ status = EP0_LL_SEND_READY;
+
+ switch (recipient) {
+ case USB_DEVREQ_RECIPIENT_DEVICE:
+ case USB_DEVREQ_RECIPIENT_INTERFACE:
+ // Nothing to do
+ break;
+ case USB_DEVREQ_RECIPIENT_ENDPOINT:
+ if ((usbs_at91_ep0.state == USBS_STATE_CONFIGURED) &&
+ (req->index_lo > 0) &&
+ (req->index_lo < AT91_USB_ENDPOINTS)) {
+ cyg_uint32 CSR;
+
+ HAL_READ_UINT32(pCSRn(req->index_lo), CSR);
+ if (CSR & AT91_UDP_CSR_EPEDS) {
+ word = 1;
+ }
+ } else {
+ status = EP0_LL_STALL;
+ }
+ break;
+ default:
+ status = EP0_LL_STALL;
+ }
+
+ *ppbegin = (cyg_uint8 *)&word;
+ *ppend = *ppbegin + sizeof (word);
+ return status;
+}
+
+// Setup the begin and end pointers such that an ACK is sent
+static void
+usbs_at91_control_setup_send_ack(void)
+{
+ cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0];
+ cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0];
+
+ *ppbegin = usbs_at91_ep0.buffer;
+ *ppend = *ppbegin;
+}
+
+// Handle a get status set feature message on the control endpoint
+static ep0_low_level_status_t
+usbs_at91_control_setup_set_feature(void)
+{
+ ep0_low_level_status_t status;
+ usb_devreq *req = (usb_devreq *)usbs_at91_ep0.control_buffer;
+ cyg_uint8 recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
+ usbs_rx_endpoint * pep;
+ cyg_uint8 ep_num = req->index_lo & 0x0F;
+
+ usbs_at91_control_setup_send_ack();
+ status = EP0_LL_SEND_READY;
+
+ switch(recipient) {
+ case USB_DEVREQ_RECIPIENT_DEVICE:
+ status = EP0_LL_STALL;
+ break;
+ case USB_DEVREQ_RECIPIENT_INTERFACE:
+ // Nothing to do
+ break;
+ case USB_DEVREQ_RECIPIENT_ENDPOINT:
+ if ((usbs_at91_ep0.state == USBS_STATE_CONFIGURED) &&
+ (ep_num > 0) &&
+ (ep_num < AT91_USB_ENDPOINTS)) {
+ cyg_uint32 CSR;
+
+ HAL_READ_UINT32(pCSRn(ep_num), CSR);
+ pep = (usbs_rx_endpoint *) usbs_at91_endpoints[ep_num];
+ if ( (CSR & AT91_UDP_CSR_EPEDS) ) {
+ usbs_at91_endpoint_set_halted ( pep , true );
+ }
+ else
+ status = EP0_LL_STALL;
+
+ }
+ else {
+ status = EP0_LL_STALL;
+ }
+ break;
+ default:
+ status = EP0_LL_STALL;
+ }
+ return status;
+}
+
+// Handle a get status clear feature message on the control endpoint
+static ep0_low_level_status_t
+usbs_at91_control_setup_clear_feature(void)
+{
+ ep0_low_level_status_t status;
+ usb_devreq *req = (usb_devreq *)usbs_at91_ep0.control_buffer;
+ cyg_uint8 recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
+ usbs_rx_endpoint * pep;
+ cyg_uint8 ep_num = req->index_lo & 0x0F;
+
+ usbs_at91_control_setup_send_ack();
+ status = EP0_LL_SEND_READY;
+
+ switch (recipient) {
+ case USB_DEVREQ_RECIPIENT_DEVICE:
+ status = EP0_LL_STALL;
+ break;
+ case USB_DEVREQ_RECIPIENT_INTERFACE:
+ // Nothing to do
+ break;
+ case USB_DEVREQ_RECIPIENT_ENDPOINT:
+ if ((usbs_at91_ep0.state == USBS_STATE_CONFIGURED) &&
+ (ep_num > 0) &&
+ (ep_num < AT91_USB_ENDPOINTS)) {
+ cyg_uint32 CSR;
+
+ HAL_READ_UINT32(pCSRn(ep_num), CSR);
+ pep = (usbs_rx_endpoint *) usbs_at91_endpoints[ep_num];
+ if ( (CSR & AT91_UDP_CSR_EPEDS) && pep->halted ) {
+ usbs_at91_endpoint_set_halted ( pep , false );
+ }
+ else
+ status = EP0_LL_STALL;
+
+ }
+ else {
+ status = EP0_LL_STALL;
+ }
+ break;
+ default:
+ status = EP0_LL_STALL;
+ }
+ return status;
+}
+
+// Handle a setup message from the host
+static ep0_low_level_status_t
+usbs_at91_control_setup(ep0_low_level_status_t status)
+{
+ cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0];
+ cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0];
+ usb_devreq *req = (usb_devreq *) usbs_at91_ep0.control_buffer;
+ cyg_uint8 protocol;
+ cyg_uint16 length;
+ bool dev_to_host;
+ usbs_control_return usbcode;
+ bool handled = false;
+
+ usbs_at91_ep0.buffer_size = 0;
+ usbs_at91_ep0.fill_buffer_fn = 0;
+ usbs_at91_ep0.complete_fn = 0;
+
+ read_fifo_uint8 ((cyg_uint8 *)req, pFDR0, sizeof (usb_devreq));
+
+ length = (req->length_hi << 8) | req->length_lo;;
+ dev_to_host = req->type & USB_DEVREQ_DIRECTION_IN;
+
+ CLEAR_BITS (pCSR0, AT91_UDP_CSR_DTGLE);
+
+ status = EP0_LL_REQUEST;
+
+ protocol = req->type & (USB_DEVREQ_TYPE_MASK);
+
+ // Set the next transfer direction
+ if (dev_to_host) {
+ SET_BITS (pCSR0, AT91_UDP_CSR_DIR); /* Set IN direction */
+ } else {
+ CLEAR_BITS (pCSR0, AT91_UDP_CSR_DIR); /* Set OUT direction */
+ }
+
+ if (protocol == USB_DEVREQ_TYPE_STANDARD) {
+ handled = true;
+ switch (req->request) {
+ case USB_DEVREQ_GET_STATUS:
+ status = usbs_at91_control_setup_get_status();
+ break;
+ case USB_DEVREQ_SET_ADDRESS:
+ // Most of the hard work is done by the hardware. We just need
+ // to send an ACK.
+ usbs_at91_control_setup_send_ack();
+ status = EP0_LL_SEND_READY;
+ break;
+ case USB_DEVREQ_SET_FEATURE:
+ status = usbs_at91_control_setup_set_feature();
+ break;
+ case USB_DEVREQ_CLEAR_FEATURE:
+ status = usbs_at91_control_setup_clear_feature();
+ break;
+ default:
+ handled = false;
+ }
+ }
+ if ((protocol != USB_DEVREQ_TYPE_STANDARD) || !handled) {
+ // Ask the layer above to process the message
+ usbcode = usbs_parse_host_get_command (&usbs_at91_ep0);
+ usbs_at91_ep0.buffer_size = MIN (usbs_at91_ep0.buffer_size, length);
+
+ *ppbegin = usbs_at91_ep0.buffer;
+ *ppend = *ppbegin + usbs_at91_ep0.buffer_size; /* Ready to send... */
+
+ if (usbcode == USBS_CONTROL_RETURN_HANDLED) { /* OK */
+ if (dev_to_host) {
+ status = EP0_LL_SEND_READY;
+ } else {
+ status = EP0_LL_RECEIVE_READY;
+ }
+ } else {
+ status = EP0_LL_STALL;
+ }
+ }
+ // Clear the setup bit so indicating we have processed the message
+ CLEAR_BITS (pCSR0, AT91_UDP_CSR_RXSETUP);
+
+ return status;
+}
+
+static ep0_low_level_status_t
+usbs_at91_control_data_recv(ep0_low_level_status_t status)
+{
+ cyg_uint32 received = 0;
+ cyg_uint32 length;
+ cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0];
+ cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0];
+ usbs_control_return usbcode;
+
+ if (status == EP0_LL_RECEIVE_READY) {
+ received = ((*(cyg_uint32 *) pCSR0) >> 16) & 0x7ff;
+ length = MIN (received, (cyg_uint32) *ppend - (cyg_uint32) *ppbegin);
+ *ppbegin = read_fifo_uint8 (*ppbegin, pFDR0, length);
+
+ if (received < usbs_at91_endpoint_fifo_size[0]) { /* Last packet ? */
+ *ppend = *ppbegin;
+ }
+
+ if (*ppbegin == *ppend) { /* All received ? */
+ usbs_at91_ep0.buffer_size =
+ (cyg_uint32) *ppend - (cyg_uint32) usbs_at91_ep0.buffer;
+ usbcode = USBS_CONTROL_RETURN_STALL;
+
+ if (usbs_at91_ep0.complete_fn) {
+ usbcode = (*usbs_at91_ep0.complete_fn) (&usbs_at91_ep0, 0);
+ }
+
+ if (usbcode == USBS_CONTROL_RETURN_HANDLED) {
+ status = EP0_LL_SEND_READY;
+ } else {
+ status = EP0_LL_STALL;
+ }
+ }
+ }
+
+ CLEAR_BITS (pCSR0, AT91_UDP_CSR_RX_DATA_BK0);
+
+ return status;
+}
+
+static ep0_low_level_status_t
+usbs_at91_control_data_sent(ep0_low_level_status_t status)
+{
+ cyg_uint8 **ppbegin = &usbs_at91_endpoint_pbegin[0];
+ cyg_uint8 **ppend = &usbs_at91_endpoint_pend[0];
+ cyg_uint32 bytes_to_write = 0;
+ usb_devreq *req = (usb_devreq *)usbs_at91_ep0.control_buffer;
+ cyg_uint16 value;
+
+ switch (status) {
+ case EP0_LL_SEND_READY:
+ if (*ppbegin == *ppend &&
+ usbs_at91_ep0.fill_buffer_fn == NULL) {
+ // All bytes are sent, send ACK
+ status = EP0_LL_ACK;
+ SET_BITS (pCSR0, AT91_UDP_CSR_TXPKTRDY); // Signal FIFO loaded
+ } else {
+ // We have more bytes to send
+ bytes_to_write =
+ MIN (*ppend - *ppbegin, usbs_at91_endpoint_fifo_size[0]);
+ *ppbegin = write_fifo_uint8 (pFDR0, *ppbegin, (cyg_uint8 *)
+ ((cyg_uint32) *ppbegin + bytes_to_write));
+ // Send next few bytes
+ if (*ppbegin == *ppend) { /* Control-Endoints don't need ACK's */
+ if (usbs_at91_ep0.fill_buffer_fn) { // More Records ?
+ (*usbs_at91_ep0.fill_buffer_fn) (&usbs_at91_ep0);
+
+ *ppbegin = usbs_at91_ep0.buffer;
+ *ppend = *ppbegin + usbs_at91_ep0.buffer_size;
+
+ /* Ready to send... */
+ bytes_to_write =
+ MIN (*ppend - *ppbegin,
+ usbs_at91_endpoint_fifo_size[0] - bytes_to_write);
+
+ *ppbegin = write_fifo_uint8 (pFDR0, *ppbegin, (cyg_uint8 *)
+ ((cyg_uint32) *ppbegin + bytes_to_write));
+ // Send next few bytes
+ } else {
+ if (bytes_to_write == usbs_at91_endpoint_fifo_size[0]) {
+ // Last packet is full, so we need to send a zero bytes
+ // packet next time
+ status = EP0_LL_SEND_READY;
+ } else {
+ status = EP0_LL_IDLE;
+ }
+
+ }
+ }
+ SET_BITS (pCSR0, AT91_UDP_CSR_TXPKTRDY); // Signal FIFO loaded
+ }
+ break;
+ case EP0_LL_RECEIVE_READY:
+ /* Maybe we have to send an ACK */
+ if (*ppbegin == *ppend) { // All bytes are received, send ACK
+ status = EP0_LL_ACK;
+ SET_BITS (pCSR0, AT91_UDP_CSR_TXPKTRDY); // Signal FIFO loaded
+ }
+ break;
+ case EP0_LL_ACK:
+ if (req->request == USB_DEVREQ_SET_ADDRESS) { // Special-processing
+ HAL_WRITE_UINT32 (AT91_UDP + AT91_UDP_FADDR,
+ req->value_lo | AT91_UDP_FADDR_FEN);
+ value = (req->value_hi << 8) | req->value_lo;
+ if (value) {
+ usbs_at91_ep0.state = USBS_STATE_ADDRESSED;
+ }
+ }
+
+ if (usbs_at91_ep0.complete_fn) {
+ (*usbs_at91_ep0.complete_fn) (&usbs_at91_ep0,
+ USBS_CONTROL_RETURN_HANDLED);
+ }
+ status = EP0_LL_IDLE;
+ usbs_state_notify (&usbs_at91_ep0);
+ break;
+ default:
+ break;
+ }
+ return status;
+}
+
+static void
+usbs_at91_control_dsr (void)
+{
+ static ep0_low_level_status_t status = EP0_LL_IDLE;
+
+ while (!BITS_ARE_CLEARED(pCSR0,
+ AT91_UDP_CSR_TXCOMP | AT91_UDP_CSR_RX_DATA_BK0 |
+ AT91_UDP_CSR_RXSETUP | AT91_UDP_CSR_ISOERROR |
+ AT91_UDP_CSR_RX_DATA_BK1)) {
+
+ // Check and handle any error conditions
+ if (BITS_ARE_SET (pCSR0, AT91_UDP_CSR_ISOERROR)) {
+ status = usbs_at91_control_error(status);
+ }
+
+ // Check for a setup message and handle it
+ if (BITS_ARE_SET (pCSR0, AT91_UDP_CSR_RXSETUP)) {
+ status = usbs_at91_control_setup(status);
+ }
+
+ // Check for received data on the control endpoint
+ if (BITS_ARE_SET (pCSR0, AT91_UDP_CSR_RX_DATA_BK0)) {
+ status = usbs_at91_control_data_recv(status);
+ }
+
+ // Check if the last packet has been sent
+ if (BITS_ARE_CLEARED (pCSR0, AT91_UDP_CSR_TXPKTRDY)) {
+ status = usbs_at91_control_data_sent(status);
+ }
+
+ // Received an ACK packet
+ if (BITS_ARE_SET (pCSR0, AT91_UDP_CSR_TXCOMP)) {
+ CLEAR_BITS (pCSR0, AT91_UDP_CSR_TXCOMP);
+ }
+
+ if (status == EP0_LL_STALL) {
+ CLEAR_BITS (pCSR0, 0x7f);
+ SET_BITS (pCSR0, AT91_UDP_CSR_FORCESTALL);
+ }
+ }
+}
+
+static void
+usbs_at91_dsr (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+ cyg_uint8 n;
+
+ CYG_ASSERT (CYGNUM_HAL_INTERRUPT_UDP == vector, "Wrong interrupts");
+ CYG_ASSERT (0 == data, "DSR needs no data");
+
+ CLEAR_BITS (AT91_UDP + AT91_UDP_GLB_STATE, 0x10);
+
+ if (BITS_ARE_SET (pISR, AT91_UDP_WAKEUP)) {
+ usbs_at91_ep0.state = USBS_STATE_DEFAULT;
+ usbs_state_notify (&usbs_at91_ep0);
+
+ HAL_WRITE_UINT32 (pICR, AT91_UDP_WAKEUP);
+ }
+
+ if (BITS_ARE_SET (pISR, AT91_UDP_ENDBUSRES)) { // RESET UDP
+ usbs_at91_ep0.state = USBS_STATE_POWERED;
+ usbs_state_notify (&usbs_at91_ep0);
+ usbs_at91_handle_reset ();
+
+ HAL_WRITE_UINT32 (pCSR0, AT91_UDP_CSR_EPEDS | AT91_UDP_CSR_EPTYPE_CTRL);
+ HAL_WRITE_UINT32 (pIER, AT91_UDP_EPINT0);
+
+ usbs_at91_ep0.state = USBS_STATE_DEFAULT;
+ usbs_state_notify (&usbs_at91_ep0);
+
+ HAL_WRITE_UINT32 (pICR, AT91_UDP_ENDBUSRES);
+ }
+
+ if (BITS_ARE_SET (pISR, AT91_UDP_SOFINT)) {
+ HAL_WRITE_UINT32 (pICR, AT91_UDP_SOFINT);
+ }
+
+ if (BITS_ARE_SET (pISR, AT91_UDP_EXTRSM)) {
+ usbs_at91_ep0.state = usbs_at91_ep0.state & ~USBS_STATE_SUSPENDED;
+ usbs_state_notify (&usbs_at91_ep0);
+ HAL_WRITE_UINT32 (pICR, AT91_UDP_EXTRSM);
+ }
+
+ if (BITS_ARE_SET (pISR, AT91_UDP_RXRSM)) {
+ usbs_at91_ep0.state = usbs_at91_ep0.state & ~USBS_STATE_SUSPENDED;
+ usbs_state_notify (&usbs_at91_ep0);
+ HAL_WRITE_UINT32 (pICR, AT91_UDP_RXRSM);
+ }
+
+ if (BITS_ARE_SET (pISR, AT91_UDP_RXSUSP)) {
+ usbs_at91_ep0.state = usbs_at91_ep0.state | USBS_STATE_SUSPENDED;
+ usbs_state_notify (&usbs_at91_ep0);
+ HAL_WRITE_UINT32 (pICR, AT91_UDP_RXSUSP);
+ }
+
+ if (BITS_ARE_SET (pISR, AT91_UDP_EPINT0)) {
+ usbs_at91_control_dsr ();
+ }
+
+ for (n = 1; n < AT91_USB_ENDPOINTS; n++) {
+ if (*(cyg_uint32 *) pIMR & (1 << n)) {
+ usbs_at91_endpoint_dsr (n);
+ }
+ }
+
+ cyg_drv_interrupt_unmask (vector);
+}
+
+static cyg_uint32
+usbs_at91_isr (cyg_vector_t vector, cyg_addrword_t data)
+{
+ cyg_uint8 n;
+ bool need_dsr = false;
+ cyg_uint32 IMR;
+ cyg_uint32 ISR;
+
+ CYG_ASSERT (CYGNUM_HAL_INTERRUPT_UDP == vector, "Wrong interrupts");
+ CYG_ASSERT (0 == data, "ISR needs no data");
+
+ HAL_READ_UINT32(pIMR, IMR);
+ HAL_READ_UINT32(pISR, ISR);
+
+ for (n = 1; n < AT91_USB_ENDPOINTS; n++) {
+ /* Do any data endpoint need a data transfer ? */
+ if (IMR & ISR & (1 << n)) {
+ need_dsr = usbs_at91_endpoint_isr (n) || need_dsr;
+ }
+ }
+ /* If we don't need any DSR re-enable interrupts and finish */
+ if (BITS_ARE_CLEARED (pISR, AT91_UDP_ALLOWED_IRQs & 0xffffff01)
+ && !need_dsr) {
+ cyg_drv_interrupt_acknowledge (vector);
+ return CYG_ISR_HANDLED;
+ }
+
+ /* Call the DSR */
+ cyg_drv_interrupt_mask (vector);
+ cyg_drv_interrupt_acknowledge (vector);
+
+ return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+}
+
+
+
+// ----------------------------------------------------------------------------
+// Polling support. It is not clear that this is going to work particularly
+// well since according to the documentation the hardware does not generate
+// NAKs automatically - instead the ISR has to set the appropriate bits
+// sufficiently quickly to avoid confusing the host.
+//
+// Calling the isr directly avoids duplicating code, but means that
+// cyg_drv_interrupt_acknowledge() will get called when not inside a
+// real interrupt handler. This should be harmless.
+
+static void
+usbs_at91_poll (usbs_control_endpoint * endpoint)
+{
+ CYG_ASSERT (endpoint == &usbs_at91_ep0, "Wrong endpoint");
+ if (CYG_ISR_CALL_DSR == usbs_at91_isr (CYGNUM_HAL_INTERRUPT_UDP, 0)) {
+ usbs_at91_dsr (CYGNUM_HAL_INTERRUPT_UDP, 0, 0);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Initialization
+//
+// This routine gets called from a prioritized static constructor during
+// eCos startup.
+
+void
+usbs_at91_init (void)
+{
+
+ cyg_uint32 reg;
+
+ HAL_READ_UINT32 (AT91_PMC + AT91_PMC_PLLR, reg);
+
+ /* Set USB divider so we have a 48MHz clock */
+#if ((CYGNUM_HAL_ARM_AT91_CLOCK_SPEED < 48120000) && \
+ (CYGNUM_HAL_ARM_AT91_CLOCK_SPEED > 47880000))
+
+ // 48MHz clock, divider set to 1
+ HAL_WRITE_UINT32 (AT91_PMC + AT91_PMC_PLLR,
+ (reg & 0x0fffffff) | AT91_PMC_PLLR_USBDIV_1);
+
+#elif ((CYGNUM_HAL_ARM_AT91_CLOCK_SPEED < 96240000) && \
+ (CYGNUM_HAL_ARM_AT91_CLOCK_SPEED > 95760000))
+
+ // 96MHz clock, divider set to 2
+ HAL_WRITE_UINT32 (AT91_PMC + AT91_PMC_PLLR,
+ (reg & 0x0fffffff) | AT91_PMC_PLLR_USBDIV_2);
+#else
+#error CYGNUM_HAL_ARM_AT91_CLOCK_SPEED is not 48, 96 or 192MHz plusminus 0.25% ...
+#endif
+
+ /* Enable USB clock */
+ HAL_WRITE_UINT32 (AT91_PMC + AT91_PMC_SCER, AT91_PMC_SCER_UDP);
+ HAL_WRITE_UINT32 (AT91_PMC + AT91_PMC_PCER, AT91_PMC_PCER_UDP);
+
+ usbs_at91_set_pullup (false);
+#ifndef CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN_NONE
+ HAL_ARM_AT91_GPIO_CFG_DIRECTION(CYGDAT_DEVS_USB_AT91_GPIO_SET_PULLUP_PIN,
+ AT91_PIN_OUT);
+#endif
+#ifndef CYGDAT_DEVS_USB_AT91_GPIO_READ_POWER_PIN_NONE
+ HAL_ARM_AT91_GPIO_CFG_DIRECTION(CYGDAT_DEVS_USB_AT91_GPIO_READ_POWER_PIN,
+ AT91_PIN_IN);
+#endif
+ usbs_at91_reset_device ();
+
+ cyg_drv_interrupt_create (CYGNUM_HAL_INTERRUPT_UDP,
+ 6, // priority
+ 0, // data
+ &usbs_at91_isr,
+ &usbs_at91_dsr,
+ &usbs_at91_intr_handle, &usbs_at91_intr_data);
+
+ cyg_drv_interrupt_attach (usbs_at91_intr_handle);
+ cyg_drv_interrupt_unmask (CYGNUM_HAL_INTERRUPT_UDP);
+
+ usbs_at91_ep0.state = USBS_STATE_POWERED;
+ usbs_state_notify (&usbs_at91_ep0);
+}
+
+// ----------------------------------------------------------------------------
+// Testing support.
+usbs_testing_endpoint usbs_testing_endpoints[] = {
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL,
+ endpoint_number : 0,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &usbs_at91_ep0,
+#ifdef CYGVAR_DEVS_USB_AT91_EP0_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "0c",
+#else
+ devtab_entry : (const char*) 0,
+#endif
+ min_size : 1, // zero-byte control transfers are meaningless
+ max_size : 0x0FFFF, // limit imposed by protocol
+ max_in_padding : 0,
+ alignment : 0
+ },
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+ endpoint_number : 1,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT,
+ endpoint : (void*) &usbs_at91_ep1,
+#ifdef CYGVAR_DEVS_USB_AT91_EP1_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "1r",
+#else
+ devtab_entry : (const char*) 0,
+#endif
+ min_size : 1,
+ max_size : -1, // No hardware or driver limitation
+ max_in_padding : 0,
+ alignment : 0
+ },
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+ endpoint_number : 2,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &usbs_at91_ep2,
+#ifdef CYGVAR_DEVS_USB_AT91_EP2_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "2w",
+#else
+ devtab_entry : (const char*) 0,
+#endif
+ min_size : 1,
+ max_size : -1, // No hardware or driver limitation
+ max_in_padding : 1, // hardware limitation
+ alignment : 0
+ },
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+ endpoint_number : 3,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &usbs_at91_ep3,
+#ifdef CYGVAR_DEVS_USB_AT91_EP3_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "3w",
+#else
+ devtab_entry : (const char*) 0,
+#endif
+ min_size : 1,
+ max_size : -1, // No hardware or driver limitation
+ max_in_padding : 1, // hardware limitation
+ alignment : 0
+ },
+ USBS_TESTING_ENDPOINTS_TERMINATOR
+};
diff --git a/ecos/packages/devs/usb/at91/current/src/usbs_at91_data.cxx b/ecos/packages/devs/usb/at91/current/src/usbs_at91_data.cxx
new file mode 100644
index 0000000..c9d0665
--- /dev/null
+++ b/ecos/packages/devs/usb/at91/current/src/usbs_at91_data.cxx
@@ -0,0 +1,256 @@
+//==========================================================================
+//
+// usbs_at91_data.cxx
+//
+// Static data for the ATMEL AT91 USB device driver
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 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): Oliver Munz
+// Contributors: bartv
+// Date: 2006-02-22
+//
+// This file contains various objects that should go into extras.o
+// rather than libtarget.a, e.g. devtab entries that would normally
+// be eliminated by the selective linking.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/infra/diag.h>
+#include <cyg/io/devtab.h>
+#include <cyg/io/usb/usbs_at91.h>
+#include <pkgconf/devs_usb_at91.h>
+
+// ----------------------------------------------------------------------------
+// Initialization. The goal here is to call usbs_at91_init()
+// early on during system startup, to take care of things like
+// registering interrupt handlers etc. which are best done
+// during system init.
+//
+// If the endpoint 0 devtab entry is available then its init()
+// function can be used to take care of this. However the devtab
+// entries are optional so an alternative mechanism must be
+// provided. Unfortunately although it is possible to give
+// a C function the constructor attribute, it cannot be given
+// an initpri attribute. Instead it is necessary to define a
+// dummy C++ class.
+
+extern "C" void usbs_at91_init(void);
+
+#ifndef CYGVAR_DEVS_USB_AT91_EP0_DEVTAB_ENTRY
+
+class usbs_at91_initialization {
+public:
+ usbs_at91_initialization() {
+ usbs_at91_init();
+ }
+};
+
+static usbs_at91_initialization CYGBLD_ATTRIB_INIT_BEFORE(CYG_INIT_IO)
+ usbs_at91_init_object;
+#endif
+
+// ----------------------------------------------------------------------------
+// The devtab entries. Each of these is optional, many applications
+// will want to use the lower-level API rather than go via
+// open/read/write/ioctl.
+
+#ifdef CYGVAR_DEVS_USB_AT91_EP0_DEVTAB_ENTRY
+// For endpoint 0 the only legal operations are get_config() and
+// set_config(), and these are provided by the common package.
+
+static bool usbs_at91_devtab_ep0_init(struct cyg_devtab_entry* tab){
+
+ CYG_UNUSED_PARAM(struct cyg_devtab_entry*, tab);
+
+ usbs_at91_init();
+
+ return true;
+}
+
+CHAR_DEVIO_TABLE(usbs_at91_ep0_devtab_functions,
+ &cyg_devio_cwrite,
+ &cyg_devio_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+CHAR_DEVTAB_ENTRY(usbs_at91_ep0_devtab_entry,
+ CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "0",
+ 0,
+ &usbs_at91_ep0_devtab_functions,
+ &usbs_at91_devtab_ep0_init,
+ 0,
+ (void*) &usbs_at91_ep0);
+#endif
+
+// ----------------------------------------------------------------------------
+// Common routines for ep1..3
+
+#if defined(CYGVAR_DEVS_USB_AT91_EP1_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_AT91_EP2_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_AT91_EP3_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_AT91_EP4_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_AT91_EP5_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_AT91_EP6_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_AT91_EP7_DEVTAB_ENTRY)
+
+static bool usbs_at91_devtab_dummy_init(struct cyg_devtab_entry* tab){
+
+ CYG_UNUSED_PARAM(struct cyg_devtab_entry*, tab);
+ return true;
+}
+#endif
+
+#ifdef CYGVAR_DEVS_USB_AT91_EP1_DEVTAB_ENTRY
+CHAR_DEVIO_TABLE(usbs_at91_ep1_devtab_functions,
+ &usbs_devtab_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+CHAR_DEVTAB_ENTRY(usbs_at91_ep1_devtab_entry,
+ CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "1",
+ 0,
+ &usbs_at91_ep1_devtab_functions,
+ &usbs_at91_devtab_dummy_init,
+ 0,
+ (void*) &usbs_at91_ep1);
+#endif
+
+#ifdef CYGVAR_DEVS_USB_AT91_EP2_DEVTAB_ENTRY
+CHAR_DEVIO_TABLE(usbs_at91_ep2_devtab_functions,
+ &usbs_devtab_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+CHAR_DEVTAB_ENTRY(usbs_at91_ep2_devtab_entry,
+ CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "2",
+ 0,
+ &usbs_at91_ep2_devtab_functions,
+ &usbs_at91_devtab_dummy_init,
+ 0,
+ (void*) &usbs_at91_ep2);
+#endif
+
+#ifdef CYGVAR_DEVS_USB_AT91_EP3_DEVTAB_ENTRY
+CHAR_DEVIO_TABLE(usbs_at91_ep3_devtab_functions,
+ &usbs_devtab_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+CHAR_DEVTAB_ENTRY(usbs_at91_ep3_devtab_entry,
+ CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "3",
+ 0,
+ &usbs_at91_ep3_devtab_functions,
+ &usbs_at91_devtab_dummy_init,
+ 0,
+ (void*) &usbs_at91_ep3);
+#endif
+
+#ifdef CYGVAR_DEVS_USB_AT91_EP4_DEVTAB_ENTRY
+CHAR_DEVIO_TABLE(usbs_at91_ep4_devtab_functions,
+ &usbs_devtab_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+CHAR_DEVTAB_ENTRY(usbs_at91_ep4_devtab_entry,
+ CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "4",
+ 0,
+ &usbs_at91_ep4_devtab_functions,
+ &usbs_at91_devtab_dummy_init,
+ 0,
+ (void*) &usbs_at91_ep4);
+#endif
+
+#ifdef CYGVAR_DEVS_USB_AT91_EP5_DEVTAB_ENTRY
+CHAR_DEVIO_TABLE(usbs_at91_ep5_devtab_functions,
+ &usbs_devtab_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+CHAR_DEVTAB_ENTRY(usbs_at91_ep5_devtab_entry,
+ CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "5",
+ 0,
+ &usbs_at91_ep5_devtab_functions,
+ &usbs_at91_devtab_dummy_init,
+ 0,
+ (void*) &usbs_at91_ep5);
+#endif
+
+#ifdef CYGVAR_DEVS_USB_AT91_EP6_DEVTAB_ENTRY
+CHAR_DEVIO_TABLE(usbs_at91_ep6_devtab_functions,
+ &usbs_devtab_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+CHAR_DEVTAB_ENTRY(usbs_at91_ep6_devtab_entry,
+ CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "6",
+ 0,
+ &usbs_at91_ep6_devtab_functions,
+ &usbs_at91_devtab_dummy_init,
+ 0,
+ (void*) &usbs_at91_ep6);
+#endif
+
+#ifdef CYGVAR_DEVS_USB_AT91_EP7_DEVTAB_ENTRY
+CHAR_DEVIO_TABLE(usbs_at91_ep7_devtab_functions,
+ &usbs_devtab_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+CHAR_DEVTAB_ENTRY(usbs_at91_ep7_devtab_entry,
+ CYGDAT_DEVS_USB_AT91_DEVTAB_BASENAME "7",
+ 0,
+ &usbs_at91_ep7_devtab_functions,
+ &usbs_at91_devtab_dummy_init,
+ 0,
+ (void*) &usbs_at91_ep7);
+#endif
diff --git a/ecos/packages/devs/usb/cortexm/stm32/current/ChangeLog b/ecos/packages/devs/usb/cortexm/stm32/current/ChangeLog
new file mode 100644
index 0000000..f7f2434
--- /dev/null
+++ b/ecos/packages/devs/usb/cortexm/stm32/current/ChangeLog
@@ -0,0 +1,41 @@
+2012-04-13 Christophe Coutand <ecos@hotmail.co.uk>
+
+ * src/usb_stm32.c (cyg_usbs_cortexm_stm32_init):
+ Peripheral clocks are enable / disable using new
+ CYGHWR_HAL_STM32_CLOCK_DISABLE / CYGHWR_HAL_STM32_CLOCK_ENABLE
+ macro supplied from the variant HAL
+
+2012-04-03 Jonathan Larmour <jifl@eCosCentric.com>
+
+ * src/usb_stm32.c (cyg_usbs_cortexm_stm32_init):
+ CYGHWR_HAL_STM32_RCC_APB1ENR_CAN was renamed to
+ CYGHWR_HAL_STM32_RCC_APB1ENR_CAN1 in variant HAL.
+
+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####
+//===========================================================================
diff --git a/ecos/packages/devs/usb/cortexm/stm32/current/cdl/usb_stm32.cdl b/ecos/packages/devs/usb/cortexm/stm32/current/cdl/usb_stm32.cdl
new file mode 100644
index 0000000..d2f35af
--- /dev/null
+++ b/ecos/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
diff --git a/ecos/packages/devs/usb/cortexm/stm32/current/include/usb_stm32.h b/ecos/packages/devs/usb/cortexm/stm32/current/include/usb_stm32.h
new file mode 100644
index 0000000..60a3905
--- /dev/null
+++ b/ecos/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
diff --git a/ecos/packages/devs/usb/cortexm/stm32/current/src/usb_stm32.c b/ecos/packages/devs/usb/cortexm/stm32/current/src/usb_stm32.c
new file mode 100644
index 0000000..dcb0ea2
--- /dev/null
+++ b/ecos/packages/devs/usb/cortexm/stm32/current/src/usb_stm32.c
@@ -0,0 +1,2456 @@
+//=============================================================================
+//
+// 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.
+ CYGHWR_HAL_STM32_CLOCK_DISABLE( CYGHWR_HAL_STM32_CAN1_CLOCK );
+
+ // 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.
+ CYGHWR_HAL_STM32_CLOCK_DISABLE( CYGHWR_HAL_STM32_USB_CLOCK );
+
+ // 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.
+ CYGHWR_HAL_STM32_CLOCK_ENABLE( CYGHWR_HAL_STM32_USB_CLOCK );
+
+ // 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
+
+//=============================================================================
diff --git a/ecos/packages/devs/usb/d12/current/ChangeLog b/ecos/packages/devs/usb/d12/current/ChangeLog
new file mode 100644
index 0000000..aeeca78
--- /dev/null
+++ b/ecos/packages/devs/usb/d12/current/ChangeLog
@@ -0,0 +1,27 @@
+2006-06-06 Frank Pagliughi <fpagliughi@mindspring.com>
+
+ * First version of the USB device driver for the philips D12
+
+//===========================================================================
+// ####GPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2006 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####
+//===========================================================================
diff --git a/ecos/packages/devs/usb/d12/current/cdl/usbs_d12.cdl b/ecos/packages/devs/usb/d12/current/cdl/usbs_d12.cdl
new file mode 100644
index 0000000..2065b12
--- /dev/null
+++ b/ecos/packages/devs/usb/d12/current/cdl/usbs_d12.cdl
@@ -0,0 +1,318 @@
+# ====================================================================
+#
+# usbs_d12.cdl
+#
+# USB device driver for the Philips PDIUSBD12 Full Speed USB
+# peripheral chip.
+#
+# ====================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2003, 2004, 2006 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): Frank M. Pagliughi (fmp), SoRo Systems, Inc.
+# Contributors:
+# Date: 2004-05-24
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_USB_D12 {
+ display "Philips D12 USB Device Driver"
+ include_dir "cyg/io/usb"
+ parent CYGPKG_USB
+ implements CYGHWR_IO_USB_SLAVE
+ doc ref/devs-usb-philips-pdiusbd12.html
+
+ description "
+ The Philips PDIUSBD12 is a USB peripheral controller (slave)
+ chip that can connect to a microcontroller or microprocessor
+ through an 8-bit parallel bus. The SoRo Systems USB-D12-104 is
+ a slave board for the PC's ISA or PC/104 bus that contains a
+ D12 chip placed in the PC's I/O space with jumpered selections
+ for IRQ and DMA settings. This package provides an eCos device
+ driver."
+
+ requires CYGIMP_DEVS_USB_D12_HW_ACCESS_HEADER
+
+ cdl_option CYGIMP_DEVS_USB_D12_HW_ACCESS_HEADER {
+ display "Inline file implementing hardware access"
+ flavor booldata
+ default_value false
+ description "
+ This option should contain the header file which
+ implements basic access to the D12 registers"
+ }
+
+ cdl_component CYGFUN_DEVS_USB_D12_EP0 {
+ display "Support the Control Endpoint 0"
+ default_value CYGINT_IO_USB_SLAVE_CLIENTS
+ requires CYGPKG_IO_USB CYGPKG_IO_USB_SLAVE
+ compile usbs_d12.c
+ compile -library=libextras.a usbs_d12_data.cxx
+ description "
+ Enable support for endpoint 0. If this support is disabled
+ then the entire USB port is unusable."
+
+ cdl_option CYGVAR_DEVS_USB_D12_EP0_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 0"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If endpoint 0 will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and ioctl calls then a devtab entry is needed.
+ "
+ }
+ cdl_option CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE {
+ display "Size of statically-allocated endpoint 0 transmit buffer"
+ flavor data
+ default_value 256
+ requires { CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE >=
+ CYGNUM_DEVS_USB_D12_EP0_PKTSIZE }
+ description "
+ The implementation of the support for endpoint 0 uses
+ a single static buffer to hold the response to the
+ current control message. Typically this buffer can be
+ fairly small since replies to control messages tend to
+ be small: typically some tens of bytes for the enumeration
+ data, perhaps a bit more for unicode-encoded string
+ descriptors. However if some application-specific protocol
+ depends on larger control messages then this buffer
+ size may need to be increased."
+ }
+ }
+
+ cdl_option CYGNUM_DEVS_USB_D12_BASEADDR {
+ display "Base Address of D12 chip"
+ flavor data
+ active_if CYGFUN_DEVS_USB_D12_EP0
+ description "
+ The base memory or I/O address where the USB chip resides.
+ The value is set by the hardware specific driver's CDL."
+ }
+
+ cdl_option CYGNUM_DEVS_USB_D12_IRQ {
+ display "IRQ for the D12 chip"
+ active_if CYGFUN_DEVS_USB_D12_EP0
+ flavor data
+ description "
+ The IRQ assigned to the D12 chip. The value
+ is set by the hardware specific drivers's CDL."
+ }
+
+ cdl_option CYGNUM_DEVS_USB_D12_INT {
+ display "INT for the D12 chip"
+ active_if CYGFUN_DEVS_USB_D12_EP0
+ flavor data
+ default_value { CYGNUM_DEVS_USB_D12_IRQ + 32 }
+ description "
+ The interrupt vector assigned to the D12 chip."
+ }
+
+ cdl_component CYGPKG_DEVS_USB_D12_THREAD {
+ display "Use a thread to service D12 chip"
+ active_if CYGFUN_DEVS_USB_D12_EP0
+ default_value 0
+ description "
+ Services the D12 USB chip with a thread, rather than at
+ the DSR level. This allows for increased debug support,
+ like TRACE output from the driver at the expense of some
+ throughput & reaction time. The service thread MUST be at
+ a higher priority than any application thread that uses
+ the USB port. "
+
+ cdl_option CYGNUM_DEVS_USB_D12_THREAD_PRIORITY {
+ display "Thread Priority"
+ flavor data
+ legal_values 1 to 30
+ default_value 4
+ description "
+ The priority of the D12 device driver thread."
+ }
+
+ cdl_option CYGNUM_DEVS_USB_D12_THREAD_STACK_SIZE {
+ display "USB Thread Stack Size"
+ flavor data
+ default_value 4096
+ description "
+ The stack size for the D12 device driver thread."
+ }
+ }
+
+ cdl_component CYGFUN_DEVS_USB_D12_DEBUG {
+ display "Debug output from the D12 Device Driver"
+ requires CYGPKG_DEVS_USB_D12_THREAD
+ default_value 0
+ description "
+ Provide debugging output from the D12 Device Driver"
+
+ cdl_option CYGSEM_DEVS_USB_D12_DEBUG_DUMP_EP0_BUFS {
+ display "Dump the contents of EP0 buffers"
+ flavor bool
+ default_value 0
+ description "
+
+ Dump the contents of the packages going through
+ EP0. This allows you to see things like device
+ requests and responses."
+ }
+
+ cdl_option CYGSEM_DEVS_USB_D12_DEBUG_DUMP_BUFS {
+ display "Dump the contents of data buffers"
+ flavor bool
+ default_value 0
+ description "
+ Dump the contents of the packages going through the generic
+ endpoints. This allow you to see all of the data going through
+ the device."
+ }
+ }
+
+ cdl_component CYGPKG_DEVS_USB_D12_TX_EP1 {
+ display "Endpoint 1 Interrupt IN, (tx_ep1)"
+ implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+ requires CYGFUN_DEVS_USB_D12_EP0
+ default_value CYGFUN_DEVS_USB_D12_EP0
+ description "
+ On the D12, Endpoint 1 IN can be used for Interrupt,
+ Bulk, or Control packages. This driver currently only supports
+ Interrupt packages on Endpoint 1 (slave -> host) transfers."
+
+ cdl_option CYGVAR_DEVS_USB_D12_TX_EP1_DEVTAB_ENTRY {
+ display "Provide a devtab entry for Endpoint 1 IN"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If Endpoint 1 IN will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and write calls then a devtab entry is needed."
+ }
+ }
+
+ cdl_component CYGPKG_DEVS_USB_D12_RX_EP1 {
+ display "Endpoint 1 Interrupt OUT, (rx_ep1)"
+ implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+ requires CYGFUN_DEVS_USB_D12_EP0
+ default_value CYGFUN_DEVS_USB_D12_EP0
+ description "
+ In the D12, Endpoint 1 OUT can be used for Interrupt,
+ Bulk, or Control packages. This driver currently only supports
+ Interrupt packages on Endpoint 1 for (host -> slave) transfers"
+
+ cdl_option CYGVAR_DEVS_USB_D12_RX_EP1_DEVTAB_ENTRY {
+ display "Provide a devtab entry for Endpoint 1 OUT"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If Endpoint 1 OUT will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and write calls then a devtab entry is needed."
+ }
+ }
+
+ cdl_component CYGPKG_DEVS_USB_D12_TX_EP2 {
+ display "Endpoint 2 Bulk IN, (tx_ep2)"
+ implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+ requires CYGFUN_DEVS_USB_D12_EP0
+ default_value CYGFUN_DEVS_USB_D12_EP0
+ description "
+ In the D12, Endpoint 2 IN can be used for Bulk, Interrupt,
+ or Control packages. This driver currently only supports
+ Bulk packages on Endpoint 2 for (slave -> host) transfers."
+
+ cdl_option CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY {
+ display "Provide a devtab entry for Endpoint 2 IN"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If Endpoint 2 IN will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and write calls then a devtab entry is needed."
+ }
+ }
+
+ cdl_component CYGPKG_DEVS_USB_D12_RX_EP2 {
+ display "Endpoint 2 Bulk OUT, (rx_ep2)"
+ implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+ requires CYGFUN_DEVS_USB_D12_EP0
+ default_value CYGFUN_DEVS_USB_D12_EP0
+ description "
+ In the D12, Endpoint 2 OUT can be used for Bulk, Interrupt,
+ Control packages. This driver currently only supports
+ Bulk packages on Endpoint 2 for (host -> slave) transfers."
+
+ cdl_option CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY {
+ display "Provide a devtab entry for Endpoint 2 OUT"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If Endpoint 2 OUT will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and write calls then a devtab entry is needed."
+ }
+ }
+
+ cdl_option CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME {
+ display "Base name for devtab entries"
+ flavor data
+ active_if { CYGVAR_DEVS_USB_D12_EP0_DEVTAB_ENTRY ||
+ CYGVAR_DEVS_USB_D12_TX_EP1_DEVTAB_ENTRY ||
+ CYGVAR_DEVS_USB_D12_RX_EP1_DEVTAB_ENTRY ||
+ CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY ||
+ CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY }
+ default_value { "\"/dev/usbs\"" }
+ description "
+ If the D12 USB device driver package provides devtab entries
+ for any of the endpoints then this option gives
+ control over the names of these entries. By default the
+ endpoints will be called \"/dev/usbs0c\", \"/dev/usbs1r\"
+ \"/dev/usbs1w\", \"/dev/usbs2r\", \"/dev/usbs2w\"
+ (assuming those endpoints are all enabled. The common
+ part \"/dev/usbs\" is determined by this configuration
+ option. It may be necessary to change this if there are
+ multiple USB slave-side devices on the target hardware to
+ prevent a name clash."
+ }
+}
+
diff --git a/ecos/packages/devs/usb/d12/current/include/usbs_d12.h b/ecos/packages/devs/usb/d12/current/include/usbs_d12.h
new file mode 100644
index 0000000..3758450
--- /dev/null
+++ b/ecos/packages/devs/usb/d12/current/include/usbs_d12.h
@@ -0,0 +1,74 @@
+#ifndef CYGONCE_USBS_D12_H
+#define CYGONCE_USBS_D12_H
+//==========================================================================
+//
+// include/usbs_d12.h
+//
+// The interface exported by the D12 USB device driver
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006 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): Frank Pagliughi (fmp)
+// Contributors: fmp
+// Date: 2004-05-24
+// Purpose:
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/io/usb/usbs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The Philips D12 is a full speed (12Mbps) USB peripheral controller
+ * chip, with a parallel interface allowing it to be connected to nearly
+ * any microcontroller or microprocessor.
+ */
+extern usbs_control_endpoint usbs_d12_ep0;
+
+extern usbs_rx_endpoint usbs_d12_rx_ep1;
+extern usbs_tx_endpoint usbs_d12_tx_ep1;
+extern usbs_rx_endpoint usbs_d12_rx_ep2;
+extern usbs_tx_endpoint usbs_d12_tx_ep2;
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+#endif /* CYGONCE_USBS_D12_H */
diff --git a/ecos/packages/devs/usb/d12/current/src/usbs_d12.c b/ecos/packages/devs/usb/d12/current/src/usbs_d12.c
new file mode 100644
index 0000000..f353cd9
--- /dev/null
+++ b/ecos/packages/devs/usb/d12/current/src/usbs_d12.c
@@ -0,0 +1,2323 @@
+//==========================================================================
+//
+// usbs_d12.c
+//
+// Driver for the D12 USB Slave Board
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006 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): Frank M. Pagliughi (fmp)
+// Date: 2004-05-22
+//
+// This code is a device driver for the SoRo Systems USB-D12-104, a PC/104
+// (ISA) Full-Speed USB slave board, which turns a PC/104 stack into a USB
+// slave device. The board contains a Philips PDIUSBD12 Peripheral Controller
+// Chip mapped into the PC's I/O space, with jumper-selectable I/O base
+// address, IRQ, and DMA settings. The eCos config tool is used to adjust
+// settings for this driver to match the physical jumper settings. The chip
+// could run in polled mode without an IRQ, but this wouldn't be a great idea
+// other than maybe a debug environment.
+//
+// The board supports DMA transfers over the Main endpoint, but I temporarily
+// removed that code to make the driver portable to other platforms.
+//
+// *** This driver should also work with the Philips ISA Eval Board
+// for the D12, but I couldn't get one of them from Philips, so
+// I couldn't test it.
+//
+// The D12 uses an indexed register set, which it describes as "commands."
+// You first write a command (index) to the command register then you can
+// read or write data to that register. Each multi-byte command read or write
+// must be dione atomically, so all access to the chip must be serialized.
+//
+// The D12 requests service through a single interrupt. The driver can
+// be configured to service the chip through a DSR or a thread. In either
+// case, the "service" code assumes it has unfettered access to the chip.
+// The interrupt, therefore never touches the chip. It just schedules the
+// DSR or service thread.
+// Currently, the code gets exclusive access to the chip by locking the
+// scheduler. This is suboptimal (locking the whole OS to touch one I/O
+// chip), and better method should be explored.
+//
+// This version of the driver does not support Isocronous transfers.
+//
+// Additional notes on the D12:
+//
+// - The D12 has 4 endpoints (2 IN, and 2 OUT) in addition to the main
+// control endpoint:
+// - Endp 0 (Control In & Out, 16 byte buffer)
+// - Endp 1 (IN & OUT, Bulk or Interrupt, 16 byte ea)
+// - Endp 2 (IN and/or OUT, Bulk, Interrupt, or Isoc, 64 bytes ea)
+//
+// - The "main" endpoint (as Philips calls it) is Endp 2. It's double
+// buffered and has a DMA interface, and thus, is suited for high
+// throughput. For applications that perform either Isoc In or Out,
+// the buffers for Endp 2 can be combined for a 128 byte space.
+// This driver, however, currently does not support this.
+//
+// - There may be a flaw in the double buffering of the rx main endpoint.
+// According to the documentation it should be invisible to the software,
+// but if both buffers fill (on an rx/OUT), they must both be read
+// together, otherwise it appears that the buffers/packets are returned
+// in reverse order. ReadMainEndpointBuf() returns the data properly.
+//
+// - All the interrupt sources on the chip - individual endpoints, bus reset,
+// suspend, and DMA - are OR'ed together and can be checked via the
+// interrupt status register. When using edge-sensitive interrupts, as
+// we do here, the ISR/DSR must be sure all interrupts are cleared before
+// returning otherwise no new interrupts will be latched.
+//
+// - If the DMA controller is not used for the Main Endpoint, you MUST enable
+// the main endpoint interrupts in the DMA register (bits 6 & 7).
+// Personally, I think this should be the default at reset, to make it
+// compatible with the other endpoints, but Philips didn't see it that
+// way.
+//
+// - When a Setup (Device Request) packet arrives in the control endpoint, a
+// bit is set in the endpoint's status register indicating the packet is
+// setup and not data. By the USB standard, a setup packet can not be
+// NAK'ed or STALL'ed, so when the chip receives a setup packet, it
+// flushes the Ctrl (EP0) IN buffer and disables the Validate and Clear
+// Buffer commands. We must send an "acknowledge setup" to both
+// EP0 IN and OUT before a Validate or Clear Buffer command is effective.
+// See ReadSetupPacket().
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+#include <cyg/infra/diag.h>
+
+#include <pkgconf/devs_usb_d12.h>
+
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_cache.h>
+#include <cyg/error/codes.h>
+
+#include <cyg/io/usb/usb.h>
+#include <cyg/io/usb/usbs.h>
+
+#include <string.h>
+
+// --------------------------------------------------------------------------
+// Common Types
+// --------------------------------------------------------------------------
+
+typedef cyg_uint8 byte;
+typedef cyg_uint8 uint8;
+typedef cyg_int16 int16;
+typedef cyg_uint16 uint16;
+typedef cyg_int32 int32;
+typedef cyg_uint32 uint32;
+
+// --------------------------------------------------------------------------
+// Tracing & Debug
+// --------------------------------------------------------------------------
+// If the driver is configured to use a thread to service the chip, then it
+// can also be configured to dump a lot of debug output.
+// Care must be taken that USB timing requirements are not violated by
+// dumping debug info. If the data is sent to a serial port, it should use
+// a hardware driver and have a large output buffer (115200 baud & 2kB
+// buffer works for me).
+
+#if defined(CYGFUN_DEVS_USB_D12_DEBUG) && CYGFUN_DEVS_USB_D12_DEBUG
+#define TRACE_D12 diag_printf
+#else
+#define TRACE_D12 (1) ? (void)0 : diag_printf
+#endif
+
+#if defined(CYGSEM_DEVS_USB_D12_DEBUG_DUMP_EP0_BUFS) && CYGSEM_DEVS_USB_D12_DEBUG_DUMP_EP0_BUFS
+#define TRACE_EP0 1
+#endif
+
+#if defined(CYGSEM_DEVS_USB_D12_DEBUG_DUMP_BUFS) && CYGSEM_DEVS_USB_D12_DEBUG_DUMP_BUFS
+#define TRACE_EP 1
+#endif
+
+#if defined(TRACE_EP0) || defined(TRACE_EP)
+static void _trace_buf(const char *hdr, const byte* buf, unsigned n)
+{
+ unsigned i;
+
+ if (buf != 0 && n != 0) {
+ if (hdr && hdr[0])
+ TRACE_D12("%s ", hdr);
+
+ TRACE_D12("[");
+ for (i=0; i<n; i++)
+ TRACE_D12(" x%02X", buf[i]);
+ TRACE_D12("]\n");
+ }
+}
+#endif
+
+#if defined(TRACE_EP0)
+#define TRACE_BUF0 _trace_buf
+#else
+#define TRACE_BUF0(hdr, buf, n)
+#endif
+
+#if defined(TRACE_EP)
+#define TRACE_BUF _trace_buf
+#else
+#define TRACE_BUF(hdr, buf, n)
+#endif
+
+// ==========================================================================
+// Chip Wrapper
+// ==========================================================================
+
+// This section contains functions that wrapper the low-level access to the
+// chip. There's a function around each register access on the chip, and then
+// some.
+
+#if defined(CYGSEM_DEVS_USB_D12_IO_MAPPED)
+typedef void* d12_addr_type;
+#else
+typedef byte* d12_addr_type;
+#endif
+
+#define D12_BASE_ADDR ((d12_addr_type) CYGNUM_DEVS_USB_D12_BASEADDR)
+
+#define D12_ENDP0_SIZE 16 // Size of Ctrl Endp
+#define D12_MAIN_ENDP 2 // The D12's "Main" Endp is special, double buffered
+#define D12_MAIN_ENDP_SIZE 64 // Size of each main endp buffer
+#define D12_MAX_PACKET_SIZE 128 // Max packet is actually double main endp
+
+#define D12_CHIP_ID 0x1012 // Value that's returned by a read of
+ //the D12's Chip ID register
+
+// ----- Endpoint Indices -----
+
+enum {
+ D12_ENDP_INVALID = 0xFF,
+ D12_ENDP_MIN = 0,
+
+ D12_RX_CTRL_ENDP = D12_ENDP_MIN, // Rx/Tx Nomenclature
+ D12_TX_CTRL_ENDP,
+
+ D12_RX_ENDP0 = D12_ENDP_MIN,
+ D12_TX_ENDP0,
+ D12_RX_ENDP1,
+ D12_TX_ENDP1,
+ D12_RX_ENDP2,
+ D12_TX_ENDP2,
+ D12_RX_MAIN_ENDP = D12_RX_ENDP2,
+ D12_TX_MAIN_ENDP = D12_TX_ENDP2,
+
+
+ D12_CTRL_ENDP_OUT = D12_ENDP_MIN, // IN/OUT Nomenclature
+ D12_CTRL_ENDP_IN,
+
+ D12_ENDP0_OUT = D12_ENDP_MIN,
+ D12_ENDP0_IN,
+ D12_ENDP1_OUT,
+ D12_ENDP1_IN,
+ D12_ENDP2_OUT,
+ D12_ENDP2_IN,
+ D12_MAIN_ENDP_OUT = D12_ENDP2_OUT,
+ D12_MAIN_ENDP_IN = D12_ENDP2_IN,
+
+ D12_ENDP_INSERT_BEFORE,
+ D12_ENDP_MAX = D12_ENDP_INSERT_BEFORE-1
+};
+
+// ----- Set Mode Reg configuration byte -----
+
+enum {
+ D12_MODE_CFG_NO_LAZYCLOCK = 0x02,
+ D12_MODE_CFG_CLOCK_RUNNING = 0x04,
+ D12_MODE_CFG_INTERRUPT = 0x08,
+ D12_MODE_CFG_SOFT_CONNECT = 0x10,
+
+ D12_MODE_CFG_NON_ISO = 0x00,
+ D12_MODE_CFG_ISO_OUT = 0x40,
+ D12_MODE_CFG_ISO_IN = 0x80,
+ D12_MODE_CFG_ISO_IO = 0xC0,
+
+ D12_MODE_CFG_DFLT = (D12_MODE_CFG_NO_LAZYCLOCK |
+ D12_MODE_CFG_CLOCK_RUNNING |
+ D12_MODE_CFG_NON_ISO)
+};
+
+// ----- Set Mode Reg clock div factor -----
+
+enum {
+ D12_MODE_CLK_24_MHZ = 1,
+ D12_MODE_CLK_16_MHZ = 2,
+ D12_MODE_CLK_12_MHZ = 3,
+ D12_MODE_CLK_8_MHZ = 5,
+ D12_MODE_CLK_6_MHZ = 7,
+ D12_MODE_CLK_4_MHZ = 11,
+
+ D12_MODE_CLK_DIV_MASK = 0x0F,
+
+ D12_MODE_CLK_SET_TO_ONE = 0x40,
+ D12_MODE_CLK_SOF_ONLY_INTR = 0x80,
+
+ D12_MODE_CLK_DFLT = (D12_MODE_CLK_4_MHZ |
+ D12_MODE_CLK_SET_TO_ONE)
+};
+
+// ----- Set DMA Register -----
+
+enum {
+ D12_DMA_SINGLE_CYCLE,
+ D12_DMA_BURST_4_CYCLE,
+ D12_DMA_BURST_8_CYCLE,
+ D12_DMA_BURST_16_CYCLE,
+
+ D12_DMA_ENABLE = 0x04,
+ D12_DMA_DIR_WRITE = 0x08,
+ D12_DMA_DIR_READ = 0x00,
+ D12_DMA_AUTO_RELOAD = 0x10,
+ D12_DMA_INTR_PIN_MODE = 0x20,
+
+ D12_DMA_MAIN_ENDP_OUT_INTR_ENABLE = 0x40,
+ D12_DMA_MAIN_RX_ENDP_INTR_ENABLE = 0x40,
+
+ D12_DMA_MAIN_ENDP_IN_INTR_ENABLE = 0x80,
+ D12_DMA_MAIN_TX_ENDP_INTR_ENABLE = 0x80,
+
+ D12_DMA_MAIN_ENDP_INTR_ENABLE = 0xC0 // Enables IN & OUT Intr
+};
+
+// ----- Interrupt Register Bits -----
+
+enum {
+ D12_INTR_RX_CTRL_ENDP = 0x0001,
+ D12_INTR_TX_CTRL_ENDP = 0x0002,
+
+ D12_INTR_RX_ENDP0 = D12_INTR_RX_CTRL_ENDP,
+ D12_INTR_TX_ENDP0 = D12_INTR_TX_CTRL_ENDP,
+ D12_INTR_RX_ENDP1 = 0x0004,
+ D12_INTR_TX_ENDP1 = 0x0008,
+ D12_INTR_RX_ENDP2 = 0x0010,
+ D12_INTR_TX_ENDP2 = 0x0020,
+
+ D12_INTR_BUS_RESET = 0x0040,
+ D12_INTR_SUSPEND_CHANGE = 0x0080,
+ D12_INTR_DMA_EOT = 0x0100
+};
+
+// ----- Read Endpoint Status -----
+
+enum {
+ D12_ENDP_STAT_SETUP_PACKET = 0x04,
+ D12_ENDP_STAT_BUF0_FULL = 0x20,
+ D12_ENDP_STAT_BUF1_FULL = 0x40,
+ D12_ENDP_STAT_ANY_BUF_FULL = 0x60,
+ D12_ENDP_STAT_BOTH_BUF_FULL = 0x60,
+ D12_ENDP_STAT_STALL = 0x80,
+};
+
+// ----- Last Transaction Status Bits -----
+
+enum {
+ D12_LAST_TRANS_DATA_SUCCESS = 0x01,
+ D12_LAST_TRANS_ERR_CODE_MASK = 0x1E,
+ D12_LAST_TRANS_SETUP_PACKET = 0x20,
+ D12_LAST_TRANS_DATA1_PACKET = 0x40,
+ D12_LAST_TRANS_PREV_STAT_NOT_READ = 0x80
+};
+
+static const byte RX_ENDP_INDEX[] =
+ { D12_RX_ENDP0, D12_RX_ENDP1, D12_RX_ENDP2 };
+static const byte TX_ENDP_INDEX[] =
+ { D12_TX_ENDP0, D12_TX_ENDP1, D12_TX_ENDP2 };
+
+static const int RX_ENDP_SIZE[] = { 16, 16, 64 };
+static const int TX_ENDP_SIZE[] = { 16, 16, 64 };
+
+typedef void (*completion_fn)(void*, int);
+
+#ifndef USB_SETUP_PACKET_LEN
+#define USB_SETUP_PACKET_LEN 8
+#endif
+
+// ----- Command Definitions -----
+
+enum {
+ CMD_SET_ADDR_EN = 0xD0, // Write 1 byte
+ CMD_SET_ENDP_EN = 0xD8, // Write 1 byte
+ CMD_SET_MODE = 0xF3, // Write 2 bytes
+ CMD_SET_DMA = 0xFB, // Write/Read 1 byte
+ CMD_READ_INTR_REG = 0xF4, // Read 2 bytes
+ CMD_SEL_ENDP = 0x00, // (+ Endp Index) Read 1 byte (opt)
+ CMD_READ_LAST_TRANS_STAT = 0x40, // (+ Endp Index) Read 1 byte (opt)
+ CMD_READ_ENDP_STAT = 0x80, // (+ Endp Index) Read 1 byte
+ CMD_READ_BUF = 0xF0, // Read n bytes
+ CMD_WRITE_BUF = 0xF0, // Write n bytes
+ CMD_SET_ENDP_STAT = 0x40, // (+ Endp Index) Write 1 byte
+ CMD_ACK_SETUP = 0xF1, // None
+ CMD_CLEAR_BUF = 0xF2, // None
+ CMD_VALIDATE_BUF = 0xFA, // None
+ CMD_SEND_RESUME = 0xF6, // None
+ CMD_READ_CURR_FRAME_NUM = 0xF5, // Read 1 or 2 bytes
+ CMD_READ_CHIP_ID = 0xFD // Read 2 bytes
+};
+
+// ----- Set Endpoint Enable Register -----
+
+enum {
+ ENDP_DISABLE,
+ ENDP_ENABLE
+};
+
+// ----- Select Endpoint Results -----
+
+enum {
+ SEL_ENDP_FULL = 0x01,
+ SEL_ENDP_STALL = 0x02
+};
+
+// ----- Error Codes from ReadLastTrans (need to be bit shifter) -----
+
+enum {
+ ERROR_NO_ERROR,
+ ERROR_PID_ENCODING,
+ ERROR_PID_UNKNOWN,
+ ERROR_UNEXPECTED_PACKET,
+ ERROR_TOKEN_CRC,
+ ERROR_DATA_CRC,
+ ERROR_TIMEOUT,
+ ERROR_BABBLE,
+ ERROR_UNEXPECTED_EOP,
+ ERROR_NAK,
+ ERROR_PACKET_ON_STALL,
+ ERROR_OVERFLOW,
+ ERROR_BITSTUFF,
+ ERROR_WRONG_DATA_PID
+};
+
+// ------------------------------------------------------------------------
+// Routines to access the D12 registers. The hardware specific driver
+// provides 8bit access functions and block access functions.
+
+#include CYGIMP_DEVS_USB_D12_HW_ACCESS_HEADER
+
+
+static inline uint16
+make_word(byte hi, byte lo)
+{
+ return ((uint16) hi << 8) | lo;
+}
+
+// These routines read or write 16 bit values to the data area.
+
+static inline uint16
+d12_read_data_word(d12_addr_type base_addr)
+{
+ uint16 val = d12_read_data_byte(base_addr);
+ val |= ((uint16) d12_read_data_byte(base_addr)) << 8;
+ return val;
+}
+
+static inline void
+d12_write_data_word(d12_addr_type base_addr, uint16 val)
+{
+ d12_write_data_byte(base_addr, (byte) val);
+ d12_write_data_byte(base_addr, (byte) (val >> 8));
+}
+
+// ------------------------------------------------------------------------
+// Command & Data I/O
+// ------------------------------------------------------------------------
+//
+// These routines read & write the registers in the D12. The procedure is
+// to write a register/command value to the command address (A0=1) then
+// read or write any required data a byte at a time to the data address
+// (A0=0). The data can be one byte or two. If two, the low byte is read/
+// written first.
+
+// NOTE: These MUST be atomic operations. It's up to the caller
+// to insure this.
+
+// The hardware specific driver provides the basic access function.
+//
+
+static inline void
+d12_write_byte(d12_addr_type base_addr, byte cmd, byte val)
+{
+ d12_write_cmd(base_addr, cmd);
+ d12_write_data_byte(base_addr, val);
+}
+
+static inline void
+d12_write_word(d12_addr_type base_addr, byte cmd, uint16 val)
+{
+ d12_write_cmd(base_addr, cmd);
+ d12_write_data_word(base_addr, val);
+}
+
+static inline byte
+d12_read_byte(d12_addr_type base_addr, byte cmd)
+{
+ d12_write_cmd(base_addr, cmd);
+ return d12_read_data_byte(base_addr);
+}
+
+static inline uint16
+d12_read_word(d12_addr_type base_addr, byte cmd)
+{
+ d12_write_cmd(base_addr, cmd);
+ return d12_read_data_word(base_addr);
+}
+
+// ------------------------------------------------------------------------
+// Higher Level Commands
+// ------------------------------------------------------------------------
+
+// Stalls or Unstalls the endpoint. Bit0=1 for stall, =0 to unstall.
+
+static inline void
+d12_set_endp_status(d12_addr_type base_addr, byte endp_idx, byte stat)
+{
+ d12_write_byte(base_addr, CMD_SET_ENDP_STAT + endp_idx, stat);
+}
+
+// ------------------------------------------------------------------------
+// Stalls the control endpoint (both in & out).
+
+static void
+d12_stall_ctrl_endp(d12_addr_type base_addr, bool stall)
+{
+ d12_set_endp_status(base_addr, D12_TX_CTRL_ENDP, stall ? 1 : 0);
+ d12_set_endp_status(base_addr, D12_RX_CTRL_ENDP, stall ? 1 : 0);
+}
+
+// ------------------------------------------------------------------------
+// Stalls/unstalls the specified endpoint.
+
+void inline
+d12_stall_endp(d12_addr_type base_addr, byte endp_idx, bool stall)
+{
+ d12_set_endp_status(base_addr, endp_idx, stall ? 1 : 0);
+}
+
+// ------------------------------------------------------------------------ */
+// Tells the chip that the selected endpoint buffer has been completely
+// read. This should be called after the application reads all the data
+// from an endpoint. While there's data in the buffer the chip will
+// automatically NAK any additional OUT packets from the host.
+
+static inline void
+d12_clear_buffer(d12_addr_type base_addr)
+{
+ d12_write_cmd(base_addr, CMD_CLEAR_BUF);
+}
+
+// ------------------------------------------------------------------------
+// Tells the chip that the data in the selected endpoint buffer is complete
+// and ready to be sent to the host.
+
+static inline void
+d12_validate_buffer(d12_addr_type base_addr)
+{
+ d12_write_cmd(base_addr, CMD_VALIDATE_BUF);
+}
+
+// ------------------------------------------------------------------------
+// Sends an upstream resume signal for 10ms. This command is normally
+// issued when the device is in suspend.
+
+static inline void
+d12_send_resume(d12_addr_type base_addr)
+{
+ d12_write_cmd(base_addr, CMD_SEND_RESUME);
+}
+
+// ------------------------------------------------------------------------
+// Gets the frame number of the last successfully received
+// start-of-frame (SOF).
+
+static inline uint16
+d12_read_curr_frame_num(d12_addr_type base_addr)
+{
+ return d12_read_word(base_addr, CMD_READ_CURR_FRAME_NUM);
+}
+
+// ------------------------------------------------------------------------
+// This routine acknowledges a setup packet by writing an Ack Setup command
+// to the currently selected Endpoint. This must be done for both EP0 out
+// and EP0 IN whenever a setup packet is received.
+
+static inline void
+d12_ack_setup(d12_addr_type base_addr)
+{
+ d12_write_cmd(base_addr, CMD_ACK_SETUP);
+}
+
+// ------------------------------------------------------------------------
+// Gets the value of the 16-bit interrupt register, which indicates the
+// source of an interrupt (if interrupts are not used, this reg can be
+// polled to find when service is required).
+
+static inline uint16
+d12_read_intr_reg(d12_addr_type base_addr)
+{
+ return d12_read_word(base_addr, CMD_READ_INTR_REG) & 0x01FF;
+}
+
+// ------------------------------------------------------------------------
+// Gets/Sets the contents of the DMA register.
+
+static inline byte
+d12_get_dma(d12_addr_type base_addr)
+{
+ return d12_read_byte(base_addr, CMD_SET_DMA);
+}
+
+static inline void
+d12_set_dma(d12_addr_type base_addr, byte mode)
+{
+ d12_write_byte(base_addr, CMD_SET_DMA, mode);
+}
+
+// ------------------------------------------------------------------------
+// Sends the "Select Endpoint" command (0x00 - 0x0D) to the chip.
+// This command initializes an internal pointer to the start of the
+// selected buffer.
+//
+// Returns: Bitfield containing status of the endpoint
+
+static byte
+d12_select_endp(d12_addr_type base_addr, byte endp_idx)
+{
+ return d12_read_byte(base_addr, CMD_SEL_ENDP + endp_idx);
+}
+
+// ------------------------------------------------------------------------
+// Gets the status of the last transaction of the endpoint. It also resets
+// the corresponding interrupt flag in the interrupt register, and clears
+// the status, indicating that it was read.
+//
+// Returns: Bitfield containing the last transaction status.
+
+static inline byte
+d12_read_last_trans_status(d12_addr_type base_addr, byte endp_idx)
+{
+ return d12_read_byte(base_addr, CMD_READ_LAST_TRANS_STAT + endp_idx);
+}
+
+// ------------------------------------------------------------------------
+// Reads the status of the requested endpoint.
+// Just for the heck of it, we mask off the reserved bits.
+//
+// Returns: Bitfield containing the endpoint status.
+
+static inline byte
+d12_read_endp_status(d12_addr_type base_addr, byte endp_idx)
+{
+ return d12_read_byte(base_addr, CMD_READ_ENDP_STAT + endp_idx) & 0xE4;
+}
+
+// ------------------------------------------------------------------------
+// Returns true if there is data available in the specified endpoint's
+// ram buffer. This is determined by the buf full flags in the endp status
+// register.
+
+static inline bool
+d12_data_available(d12_addr_type base_addr, byte endp_idx)
+{
+ byte by = d12_read_endp_status(base_addr, endp_idx);
+ return (bool) (by & D12_ENDP_STAT_ANY_BUF_FULL);
+}
+
+// ------------------------------------------------------------------------
+// Clears the transaction status for each of the endpoints by calling the
+// d12_read_last_trans_status() function for each.
+
+static void
+d12_clear_all_intr(d12_addr_type base_addr)
+{
+ uint8 endp;
+
+ d12_read_intr_reg(base_addr);
+
+ for (endp=D12_ENDP_MIN; endp<=D12_ENDP_MAX; ++endp)
+ d12_read_last_trans_status(base_addr, endp);
+}
+
+// ------------------------------------------------------------------------
+// Loads a value into the Set Address / Enable register. This sets the
+// device's USB address (lower 7 bits) and enables/disables the function
+// (msb).
+
+static void
+d12_set_addr_enable(d12_addr_type base_addr, byte usb_addr, bool enable)
+{
+ if (enable)
+ usb_addr |= 0x80;
+
+ d12_write_byte(base_addr, CMD_SET_ADDR_EN, usb_addr);
+}
+
+// ------------------------------------------------------------------------
+// Enables/disables the generic endpoints.
+
+static inline void
+d12_set_endp_enable(d12_addr_type base_addr, bool enable)
+{
+ d12_write_byte(base_addr, CMD_SET_ENDP_EN,
+ (enable) ? ENDP_ENABLE : ENDP_DISABLE);
+}
+
+// ------------------------------------------------------------------------
+// Sets the device's configuration and CLKOUT frequency.
+
+static void
+d12_set_mode(d12_addr_type base_addr, byte config, byte clk_div)
+{
+ uint16 w = make_word(clk_div, config);
+ d12_write_word(base_addr, CMD_SET_MODE, w);
+}
+
+// ------------------------------------------------------------------------
+// Reads a setup packet from the control endpoint. This procedure is
+// somewhat different than reading a data packet. By the USB standard, a
+// setup packet can not be NAK'ed or STALL'ed, so when the chip receives a
+// setup packet, it flushes the Ctrl (EP0) IN buffer and disables the
+// Validate and Clear Buffer commands. The processor must send an
+// acknowledge setup to both EP0 IN and OUT before a Validate or Clear
+// Buffer command is effective.
+//
+// Parameters:
+// buf buffer to receive the contents of the setup packet. Must
+// be at least 8 bytes.
+// Returns:
+// true if there are 8 bytes waiting in the EP0 OUT RAM buffer
+// on the D12 (i.e., true if successful)
+// false otherwise
+
+static bool
+d12_read_setup_packet(d12_addr_type base_addr, byte *buf)
+{
+ uint8 n;
+
+ d12_select_endp(base_addr, D12_RX_CTRL_ENDP);
+
+ d12_read_byte(base_addr, CMD_READ_BUF); // Read & discard reserved byte
+ n = d12_read_data_byte(base_addr); // # bytes available
+
+ if (n > USB_SETUP_PACKET_LEN) {
+ //TRACE("* Warning: Setup Packet too large: %u *\n", (unsigned) n);
+ n = USB_SETUP_PACKET_LEN;
+ }
+
+ n = d12_read_data(base_addr, buf, n);
+
+ d12_ack_setup(base_addr);
+ d12_clear_buffer(base_addr);
+
+ // ----- Ack Setup to EP0 IN ------
+
+ d12_select_endp(base_addr, D12_TX_CTRL_ENDP);
+ d12_ack_setup(base_addr);
+
+ return n == USB_SETUP_PACKET_LEN;
+}
+
+// ------------------------------------------------------------------------
+// Reads the contents of the currently selected endpoint's RAM buffer into
+// the buf[] array.
+//
+// The D12's buffer comes in as follows:
+// [0] junk ("reserved" - can be anything). Just disregard
+// [1] # data bytes to follow
+// [2] data byte 0, ...
+// up to
+// [N+2] data byte N-1
+//
+// Parameters:
+// buf byte array to receive data. This MUST be at least the size
+// of the chip's RAM buffer for the currently selected endpoint.
+// If buf is NULL, the data is read & discarded.
+//
+// Returns: the actual number of bytes read (could be <= n)
+
+static uint8
+d12_read_selected_endp_buf(d12_addr_type base_addr, byte *buf)
+{
+ uint8 n;
+
+ d12_read_byte(base_addr, CMD_READ_BUF); // Read & discard reserved byte
+ n = d12_read_data_byte(base_addr); // # bytes in chip's buf
+ d12_read_data(base_addr, buf, n);
+ d12_clear_buffer(base_addr);
+
+ return n;
+}
+
+// ------------------------------------------------------------------------
+// Selects the specified endpoint and reads the contents of it's RAM buffer
+// into the buf[] array. For the Main OUT endpoint, it will check whether
+// both buffers are full, and if so, read them both.
+//
+// Side Effects:
+// - Leaves endp_idx as the currently selected endpoint.
+//
+// Parameters:
+// endp_idx the endpoint from which to read
+// buf buffer to receive the data. This MUST be at least the size
+// of the chip's RAM buffer for the specified endpoint.
+// For the Main endp, it must be 2x the buffer size (128 total)
+//
+// Returns: the # of bytes read.
+
+static uint8
+d12_read_endp_buf(d12_addr_type base_addr, byte endp_idx, byte *buf)
+{
+ return (d12_select_endp(base_addr, endp_idx) & SEL_ENDP_FULL)
+ ? d12_read_selected_endp_buf(base_addr, buf) : 0;
+}
+
+// ------------------------------------------------------------------------
+// Does a read of the "main" endpoint (#2). Since it's double buffered,
+// this will check if both buffers are full, and if so it will read them
+// both. Thus the caller's buffer, buf, must be large enough to hold all
+// the data - 128 bytes total.
+//
+// If either buffer contains less than the full amount, the done flag
+// is set indicating that a Bulk OUT transfer is complete.
+//
+// This determines if a bulk transfer is done, since the caller can't
+// necessarily determine this from the size of the return buffer.
+// If either buffer is less than full, '*done' is set to a non-zero value.
+
+static uint8
+d12_read_main_endp_buf(d12_addr_type base_addr, byte *buf, int *done)
+{
+ int nBuf = 1;
+ uint8 n = 0;
+ byte stat = d12_read_endp_status(base_addr, D12_RX_MAIN_ENDP) &
+ D12_ENDP_STAT_ANY_BUF_FULL;
+
+ if (stat == 0)
+ return 0;
+
+ if (stat == D12_ENDP_STAT_BOTH_BUF_FULL)
+ nBuf++;
+
+ *done = false;
+
+ while (nBuf--) {
+ if (d12_select_endp(base_addr, D12_RX_MAIN_ENDP) & SEL_ENDP_FULL) {
+ uint8 n1 = d12_read_selected_endp_buf(base_addr, buf+n);
+ n += n1;
+ if (n1 < D12_MAIN_ENDP_SIZE) {
+ *done = true;
+ break;
+ }
+ }
+ else
+ *done = true;
+ }
+ return n;
+}
+
+// ------------------------------------------------------------------------
+// Writes the contents of the buf[] array to the currently selected
+// endpoint's RAM buffer. The host will get the data on the on the next IN
+// packet from the endpoint.
+//
+// Note:
+// - The length of the buffer, n, must be no more than the size of the
+// endpoint's RAM space, though, currently, this is not checked.
+// - It's feasible that the application needs to send an empty (NULL)
+// packet. It's valid for 'n' to be zero, and/or buf NULL.
+
+static uint8
+d12_write_selected_endp_buf(d12_addr_type base_addr, const byte *buf, uint8 n)
+{
+ d12_write_byte(base_addr, CMD_WRITE_BUF, 0);
+ d12_write_data_byte(base_addr, n);
+ d12_write_data(base_addr, buf, n);
+ d12_validate_buffer(base_addr);
+
+ return n;
+}
+
+// ------------------------------------------------------------------------
+// Writes the contents of the buf[] array to the specified endoint's RAM
+// buffer. The host will get this data on the next IN packet from the
+// endpoint.
+//
+// Side Effects:
+// - Leaves endp_idx as the currently selected endpoint.
+
+static uint8
+d12_write_endp_buf(d12_addr_type base_addr, byte endp_idx,
+ const byte *buf, uint8 n)
+{
+ d12_select_endp(base_addr, endp_idx);
+ return d12_write_selected_endp_buf(base_addr, buf, n);
+}
+
+// ------------------------------------------------------------------------
+// Reads & returns the contents of the Chip ID register.
+
+static inline uint16
+d12_read_chip_id(d12_addr_type base_addr)
+{
+ return d12_read_word(base_addr, CMD_READ_CHIP_ID);
+}
+
+
+// ==========================================================================
+// eCos-Specific Device Driver Code
+// ==========================================================================
+
+static void usbs_d12_reset(void);
+
+// Make some abbreviations for the configuration options.
+
+#if defined(CYGPKG_DEVS_USB_D12_RX_EP1)
+#define _RX_EP1
+#endif
+
+#if defined(CYGPKG_DEVS_USB_D12_TX_EP1)
+#define _TX_EP1
+#endif
+
+#if defined(CYGPKG_DEVS_USB_D12_RX_EP2)
+#define _RX_EP2
+#endif
+
+#if defined(CYGPKG_DEVS_USB_D12_TX_EP2)
+#define _TX_EP2
+#endif
+
+// --------------------------------------------------------------------------
+// Endpoint 0 Data
+// --------------------------------------------------------------------------
+
+static cyg_interrupt usbs_d12_intr_data;
+static cyg_handle_t usbs_d12_intr_handle;
+
+static byte ep0_tx_buffer[CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE];
+
+static void usbs_d12_start(usbs_control_endpoint*);
+static void usbs_d12_poll(usbs_control_endpoint*);
+
+typedef enum endp_state {
+ ENDP_STATE_IDLE,
+ ENDP_STATE_IN,
+ ENDP_STATE_OUT
+} endp_state;
+
+typedef struct ep0_impl {
+ usbs_control_endpoint common;
+ endp_state ep_state;
+ int length;
+ int transmitted;
+ bool tx_empty;
+} ep0_impl;
+
+static ep0_impl ep0 = {
+ common:
+ {
+ state: USBS_STATE_POWERED,
+ enumeration_data: (usbs_enumeration_data*) 0,
+ start_fn: &usbs_d12_start,
+ poll_fn: &usbs_d12_poll,
+ interrupt_vector: CYGNUM_DEVS_USB_D12_IRQ,
+ control_buffer: { 0, 0, 0, 0, 0, 0, 0, 0 },
+ state_change_fn: 0,
+ state_change_data: 0,
+ standard_control_fn: 0,
+ standard_control_data: 0,
+ class_control_fn: 0,
+ class_control_data: 0,
+ vendor_control_fn: 0,
+ vendor_control_data: 0,
+ reserved_control_fn: 0,
+ reserved_control_data: 0,
+ buffer: 0,
+ buffer_size: 0,
+ fill_buffer_fn: 0,
+ fill_data: 0,
+ fill_index: 0,
+ complete_fn: 0
+ },
+ ep_state: ENDP_STATE_IDLE,
+ length: 0,
+ transmitted: 0,
+ tx_empty: 0
+};
+
+extern usbs_control_endpoint usbs_d12_ep0 __attribute__((alias ("ep0")));
+
+// --------------------------------------------------------------------------
+// Rx Endpoints 1 & 2 Data
+// --------------------------------------------------------------------------
+
+#if defined(_RX_EP1) || defined(_RX_EP2)
+
+typedef struct rx_endpoint {
+ usbs_rx_endpoint common;
+ int endp, received;
+} rx_endpoint;
+
+static void usbs_d12_api_start_rx_ep(usbs_rx_endpoint*);
+static void usbs_d12_api_stall_rx_ep(usbs_rx_endpoint*, cyg_bool);
+
+static void usbs_d12_ep_rx_complete(rx_endpoint *ep, int result);
+static void usbs_d12_stall_rx_ep(rx_endpoint*, cyg_bool);
+
+#endif
+
+
+#if defined(_RX_EP1)
+
+static rx_endpoint rx_ep1 = {
+ common: {
+ start_rx_fn: &usbs_d12_api_start_rx_ep,
+ set_halted_fn: &usbs_d12_api_stall_rx_ep,
+ halted: 0
+ },
+ endp: 1
+};
+
+extern usbs_rx_endpoint usbs_d12_rx_ep1 __attribute__((alias ("rx_ep1")));
+
+#endif
+
+
+#if defined(_RX_EP2)
+
+static rx_endpoint rx_ep2 = {
+ common: {
+ start_rx_fn: &usbs_d12_api_start_rx_ep,
+ set_halted_fn: &usbs_d12_api_stall_rx_ep,
+ halted: 0
+ },
+ endp: 2
+};
+
+extern usbs_rx_endpoint usbs_d12_rx_ep2 __attribute__((alias ("rx_ep2")));
+
+#endif
+
+// --------------------------------------------------------------------------
+// Tx Endpoints 1 & 2 Data
+// --------------------------------------------------------------------------
+
+#if defined(_TX_EP1) || defined(_TX_EP2)
+
+typedef struct tx_endpoint {
+ usbs_tx_endpoint common;
+ int endp, transmitted;
+ bool tx_empty;
+} tx_endpoint;
+
+static void usbs_d12_api_start_tx_ep(usbs_tx_endpoint*);
+static void usbs_d12_api_stall_tx_ep(usbs_tx_endpoint*, cyg_bool);
+
+static void usbs_d12_ep_tx_complete(tx_endpoint *ep, int result);
+static void usbs_d12_stall_tx_ep(tx_endpoint*, cyg_bool);
+#endif
+
+#if defined(_TX_EP1)
+
+static tx_endpoint tx_ep1 = {
+ common: {
+ start_tx_fn: &usbs_d12_api_start_tx_ep,
+ set_halted_fn: &usbs_d12_api_stall_tx_ep,
+ halted: 0
+ },
+ endp: 1
+};
+
+extern usbs_tx_endpoint usbs_d12_tx_ep1 __attribute__((alias ("tx_ep1")));
+#endif
+
+#if defined(_TX_EP2)
+
+static tx_endpoint tx_ep2 = {
+ common: {
+ start_tx_fn: &usbs_d12_api_start_tx_ep,
+ set_halted_fn: &usbs_d12_api_stall_tx_ep,
+ halted: 0
+ },
+ endp: 2
+};
+
+extern usbs_tx_endpoint usbs_d12_tx_ep2 __attribute__((alias ("tx_ep2")));
+
+#endif
+
+// --------------------------------------------------------------------------
+// Synchronization
+
+static inline void usbs_d12_lock(void) { cyg_scheduler_lock(); }
+static inline void usbs_d12_unlock(void) { cyg_scheduler_unlock(); }
+
+// --------------------------------------------------------------------------
+// Control Endpoint
+// --------------------------------------------------------------------------
+
+// Fills the EP0 transmit buffer with a packet. Partial data packets are
+// retrieved by repeatedly calling the fill function.
+
+static int
+ep0_fill_tx_buffer(void)
+{
+ int nFilled = 0;
+
+ while (nFilled < CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE) {
+ if (ep0.common.buffer_size != 0) {
+ if ((nFilled + ep0.common.buffer_size) <
+ CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE) {
+ memcpy(&ep0_tx_buffer[nFilled], ep0.common.buffer,
+ ep0.common.buffer_size);
+ nFilled += ep0.common.buffer_size;
+ ep0.common.buffer_size = 0;
+ }
+ else {
+ break;
+ }
+ }
+ else if (ep0.common.fill_buffer_fn) {
+ (*ep0.common.fill_buffer_fn)(&ep0.common);
+ }
+ else {
+ break;
+ }
+ }
+ CYG_ASSERT((ep0.common.buffer_size == 0) && (!ep0.common.fill_buffer_fn),
+ "EP0 transmit buffer overflow");
+ TRACE_D12("EP0: Filled Tx Buf with %d bytes\n", nFilled);
+
+ ep0.length = nFilled;
+
+ ep0.common.fill_buffer_fn = 0;
+ ep0.common.fill_data = 0;
+ ep0.common.fill_index = 0;
+
+ return nFilled;
+}
+
+// --------------------------------------------------------------------------
+// Called when a transfer is complete on the control endpoint EP0.
+// It resets the endpoint's data structure and calls the completion function,
+// if any.
+//
+// PARAMETERS:
+// result 0, on success
+// -EPIPE or -EIO to indicate a cancellation
+
+static usbs_control_return
+usbs_d12_ep0_complete(int result)
+{
+ usbs_control_return ret = USBS_CONTROL_RETURN_UNKNOWN;
+
+ ep0.ep_state = ENDP_STATE_IDLE;
+
+ if (ep0.common.complete_fn)
+ ret = (*ep0.common.complete_fn)(&ep0.common, result);
+
+ ep0.common.buffer = 0;
+ ep0.common.buffer_size = 0;
+ ep0.common.complete_fn = 0;
+ //ep0.common.fill_buffer_fn = 0;
+
+ return ret;
+}
+
+// --------------------------------------------------------------------------
+// This routine is called when we want to send the next packet to the tx ep0
+// on the chip. It is used to start a new transfer, and is also called when
+// the chip interrupts to indicate that the ep0 tx buffer is empty and ready
+// to receive a new packet.
+//
+// NOTE:
+// On the D12, when you send a zero-length packet to a tx endpoint, the
+// chip transmits the empty packet to the host, but doesn't interrupt
+// indicating that it is complete. So immediately after sending the
+// empty packet we complete the transfer.
+
+static void
+usbs_d12_ep0_tx(void)
+{
+ int nRemaining = ep0.length - ep0.transmitted;
+ uint8 n;
+
+ // ----- Intermittent interrupt? Get out -----
+
+ if (!ep0.common.buffer) {
+ TRACE_D12("EP0: Tx no buffer (%d)\n", nRemaining);
+ return;
+ }
+
+ // ----- If prev packet was last, signal that we're done -----
+
+ if (nRemaining == 0 && !ep0.tx_empty) {
+ TRACE_D12("\tEP0: Tx Complete (%d) %p\n", ep0.transmitted,
+ ep0.common.complete_fn);
+ usbs_d12_ep0_complete(0);
+ return;
+ }
+
+ // ----- Load the next tx packet onto the chip -----
+
+ if (nRemaining < D12_ENDP0_SIZE) {
+ n = (uint8) nRemaining;
+ ep0.tx_empty = false;
+ }
+ else
+ n = D12_ENDP0_SIZE;
+
+ d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0,
+ &ep0_tx_buffer[ep0.transmitted], n);
+
+ TRACE_D12("EP0: Wrote %u bytes\n", (unsigned) n);
+ TRACE_BUF0("\t", &ep0_tx_buffer[ep0.transmitted], n);
+
+ ep0.transmitted += n;
+
+ // ----- If empty packet, D12 won't interrupt, so end now ----- */
+
+ if (n == 0) {
+ TRACE_D12("\tEP0: Tx Complete (%d) %p\n", ep0.transmitted,
+ ep0.common.complete_fn);
+ usbs_d12_ep0_complete(0);
+ }
+}
+
+// --------------------------------------------------------------------------
+// This function is called when a packet has been successfully sent on the
+// primary control endpoint (ep0). It indicates that the chip is ready for
+// another packet. We read the LastTransStatus for the endpoint to clear
+// the interrupt bit, then call ep0_tx() to continue the transfer.
+
+static void
+usbs_d12_ep0_tx_intr(void)
+{
+ d12_read_last_trans_status(D12_BASE_ADDR, D12_TX_ENDP0);
+ usbs_d12_ep0_tx();
+}
+
+// --------------------------------------------------------------------------
+// Try to handle standard requests. This is a three step process:
+// 1. If it's something we should handle internally we take care of it.
+// Currently we can handle SET_ADDRESS requests, and a few others.
+// 2. If the upper level code has installed a standard control handler
+// we let that function have a crack at it.
+// 3. If neither of those handle the packet we let
+// usbs_handle_standard_control() have a last try at it.
+//
+// Locally:
+// SET_ADDRESS: The host is demanding that we change our USB address.
+// This is done by updating the Address/Enable register on the D12.
+// Note, however that the USB protocol requires us to ack at the old
+// address, change address, and then accept the next control message
+// at the new address. The D12 address reg is buffered to do this
+// automatically for us. The updated address on the chip won't take
+// affect until after the empty ack is sent. Nice.
+//
+
+static usbs_control_return
+usbs_d12_handle_std_req(usb_devreq *req)
+{
+ usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+ int recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
+
+ if (req->request == USB_DEVREQ_SET_ADDRESS) {
+ TRACE_D12("Setting Addr: %u\n", (unsigned) req->value_lo);
+ d12_set_addr_enable(D12_BASE_ADDR, req->value_lo, true);
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+ else if (req->request == USB_DEVREQ_GET_STATUS) {
+ if (recipient == USB_DEVREQ_RECIPIENT_DEVICE) {
+ const usbs_enumeration_data *enum_data = ep0.common.enumeration_data;
+ if (enum_data && enum_data->device.number_configurations == 1 &&
+ enum_data->configurations) {
+ ep0.common.control_buffer[0] =
+ (enum_data->configurations[0].attributes
+ & USB_CONFIGURATION_DESCRIPTOR_ATTR_SELF_POWERED) ? 1 : 0;
+ ep0.common.control_buffer[0] |=
+ (enum_data->configurations[0].attributes
+ & USB_CONFIGURATION_DESCRIPTOR_ATTR_REMOTE_WAKEUP) ? 2 : 0;
+ ep0.common.control_buffer[1] = 0;
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+ }
+ else if (recipient == USB_DEVREQ_RECIPIENT_ENDPOINT) {
+ bool halted = false;
+ result = USBS_CONTROL_RETURN_HANDLED;
+
+ switch (req->index_lo) {
+#if defined(_RX_EP1)
+ case 0x01 : halted = rx_ep1.common.halted; break;
+#endif
+#if defined(_TX_EP1)
+ case 0x81 : halted = tx_ep1.common.halted; break;
+#endif
+#if defined(_RX_EP2)
+ case 0x02 : halted = rx_ep2.common.halted; break;
+#endif
+#if defined(_TX_EP2)
+ case 0x82 : halted = tx_ep2.common.halted; break;
+#endif
+
+ default:
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+
+ TRACE_D12("Get Status: Endp [0x%02X] %s\n", (unsigned) req->index_lo,
+ halted ? "Halt" : "Unhalt");
+ if (result == USBS_CONTROL_RETURN_HANDLED) {
+ ep0.common.control_buffer[0] = (halted) ? 1 : 0;
+ ep0.common.control_buffer[1] = 0;
+ }
+ }
+
+ if (result == USBS_CONTROL_RETURN_HANDLED) {
+ ep0.common.buffer = ep0.common.control_buffer;
+ ep0.common.buffer_size = 2;
+ ep0.common.fill_buffer_fn = 0;
+ ep0.common.complete_fn = 0;
+ }
+ }
+ else if ((req->request == USB_DEVREQ_SET_FEATURE ||
+ req->request == USB_DEVREQ_CLEAR_FEATURE) &&
+ recipient == USB_DEVREQ_RECIPIENT_ENDPOINT) {
+
+ bool halt = (req->request == USB_DEVREQ_SET_FEATURE);
+ result = USBS_CONTROL_RETURN_HANDLED;
+ TRACE_D12("Endpoint [0x%02X] %s\n", (unsigned) req->index_lo,
+ halt ? "Halt" : "Unhalt");
+
+ switch (req->index_lo) {
+#if defined(_RX_EP1)
+ case 0x01 : usbs_d12_stall_rx_ep(&rx_ep1, halt); break;
+#endif
+#if defined(_TX_EP1)
+ case 0x81 : usbs_d12_stall_tx_ep(&tx_ep1, halt); break;
+#endif
+#if defined(_RX_EP2)
+ case 0x02 : usbs_d12_stall_rx_ep(&rx_ep2, halt); break;
+#endif
+#if defined(_TX_EP2)
+ case 0x82 : usbs_d12_stall_tx_ep(&tx_ep2, halt); break;
+#endif
+
+ default:
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+ }
+ else if (ep0.common.standard_control_fn != 0) {
+ result = (*ep0.common.standard_control_fn)
+ (&ep0.common,
+ ep0.common.standard_control_data);
+ }
+
+ if (result == USBS_CONTROL_RETURN_UNKNOWN)
+ result = usbs_handle_standard_control(&ep0.common);
+
+ return result;
+}
+
+// --------------------------------------------------------------------------
+// Handler for the receipt of a setup (dev request) packet from the host.
+// We examine the packet to determine what function(s) should get a crack
+// at trying to handle it, then pass control to the proper function. If
+// the function handles the message we either ACK (len==0) or prepare for
+// an IN or OUT data phase. If no one handled the message, we stall the
+// control endpoint.
+
+static void
+usbs_d12_ep0_setup_packet(usb_devreq* req)
+{
+ int len, dir, protocol, recipient;
+ usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+
+ // ----- See who should take the request -----
+
+ len = make_word(req->length_hi, req->length_lo);
+
+ dir = req->type & USB_DEVREQ_DIRECTION_MASK;
+ protocol = req->type & USB_DEVREQ_TYPE_MASK;
+ recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
+
+ TRACE_BUF0("DevReq: ", ep0.common.control_buffer, sizeof(usb_devreq));
+
+ if (protocol == USB_DEVREQ_TYPE_STANDARD)
+ result = usbs_d12_handle_std_req(req);
+ else {
+ // Pass on non-standard requests to registered handlers
+
+ usbs_control_return (*callback_fn)(usbs_control_endpoint*, void*);
+ void *callback_arg;
+
+ if (protocol == USB_DEVREQ_TYPE_CLASS) {
+ callback_fn = ep0.common.class_control_fn;
+ callback_arg = ep0.common.class_control_data;
+ }
+ else if (protocol == USB_DEVREQ_TYPE_VENDOR) {
+ callback_fn = ep0.common.vendor_control_fn;
+ callback_arg = ep0.common.vendor_control_data;
+ }
+ else {
+ callback_fn = ep0.common.reserved_control_fn;
+ callback_arg = ep0.common.reserved_control_data;
+ }
+
+ result = (callback_fn) ? (*callback_fn)(&ep0.common, callback_arg)
+ : USBS_CONTROL_RETURN_STALL;
+ }
+
+ // ----- If handled prep/handle data phase, otherwise stall -----
+
+ if (result == USBS_CONTROL_RETURN_HANDLED) {
+ if (len == 0) {
+ TRACE_D12("\tCtrl ACK\n");
+ d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0, 0, 0);
+ }
+ else {
+ // Set EP0 state to IN or OUT mode for data phase
+ ep0.transmitted = 0;
+ ep0.length = len;
+
+ if (dir == USB_DEVREQ_DIRECTION_OUT) {
+ // Wait for the next packet from the host.
+ ep0.ep_state = ENDP_STATE_OUT;
+ CYG_ASSERT(ep0.common.buffer != 0,
+ "A rx buffer should have been provided for EP0");
+ CYG_ASSERT(ep0.common.complete_fn != 0,
+ "A completion function should be provided for EP0 OUT control messages");
+ }
+ else {
+ ep0.tx_empty = true;
+ ep0.ep_state = ENDP_STATE_IN;
+ ep0_fill_tx_buffer();
+ usbs_d12_ep0_tx();
+ }
+ }
+ }
+ else {
+ TRACE_D12("\t*** Unhandled Device Request ***\n");
+ // The request wasn't handled, so stall control endpoint
+ d12_stall_ctrl_endp(D12_BASE_ADDR, true);
+ }
+}
+
+// --------------------------------------------------------------------------
+// This is called when the chip indicates that a packet has been received
+// on control endpoint 0. If it's a setup packet, we handle it accordingly,
+// otherwise it's a data packet coming in on ep0.
+//
+
+static void
+usbs_d12_ep0_rx_intr(void)
+{
+ byte byStat = d12_read_last_trans_status(D12_BASE_ADDR, D12_RX_ENDP0);
+ TRACE_D12("\tEP0 Status: 0x%02X\n", (unsigned) byStat);
+
+ if (byStat & D12_LAST_TRANS_SETUP_PACKET) {
+ usb_devreq *req = (usb_devreq *) ep0.common.control_buffer;
+
+ if (!d12_read_setup_packet(D12_BASE_ADDR, (byte*) req)) {
+ TRACE_D12("ep0_rx_dsr: Error reading setup packet\n");
+ d12_stall_ctrl_endp(D12_BASE_ADDR, true);
+ }
+ else
+ usbs_d12_ep0_setup_packet(req);
+ }
+ else {
+ if (ep0.common.buffer) {
+ uint8 n = d12_read_endp_buf(D12_BASE_ADDR, D12_RX_ENDP0,
+ ep0.common.buffer + ep0.transmitted);
+ ep0.transmitted += n;
+
+ TRACE_D12("EP0: Received %d bytes\n", (unsigned) n);
+
+ if (n < D12_ENDP0_SIZE ||
+ ep0.common.buffer_size - ep0.transmitted < D12_ENDP0_SIZE) {
+ TRACE_D12("\tEP0: Rx Complete (%d) %p\n",
+ ep0.transmitted, ep0.common.complete_fn);
+
+ if (usbs_d12_ep0_complete(0) == USBS_CONTROL_RETURN_HANDLED)
+ d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0, 0, 0);
+ else
+ d12_stall_ctrl_endp(D12_BASE_ADDR, true);
+ }
+ }
+ else {
+ TRACE_D12("EP0: No Rx buffer. Discarding packet\n");
+ d12_read_endp_buf(D12_BASE_ADDR, D12_RX_ENDP0, NULL);
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+// Handler for when the device is put into or taken out of suspend mode.
+// It updates the state variable in the control endpoint and calls the
+// registered state change function, if any.
+
+// TODO: Put the chip into low power mode??? Stop clocks, etc???
+
+static void
+usbs_d12_suspend(bool suspended)
+{
+ int old_state = ep0.common.state;
+ usbs_state_change state_change;
+
+ if (suspended) {
+ ep0.common.state |= USBS_STATE_SUSPENDED;
+ state_change = USBS_STATE_CHANGE_SUSPENDED;
+ }
+ else {
+ ep0.common.state &= USBS_STATE_MASK;
+ state_change = USBS_STATE_CHANGE_RESUMED;
+ }
+
+ if (ep0.common.state_change_fn) {
+ (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+ state_change, old_state);
+ }
+}
+
+// --------------------------------------------------------------------------
+// Common Rx Endpoint 1 & 2
+// --------------------------------------------------------------------------
+
+#if defined(_RX_EP1) || defined(_RX_EP2)
+
+static void usbs_d12_clear_rx_ep(rx_endpoint *ep)
+{
+ ep->common.buffer = 0;
+ ep->common.buffer_size = 0;
+ ep->common.complete_fn = 0;
+ ep->common.complete_data = 0;
+
+ ep->received = 0;
+}
+
+// --------------------------------------------------------------------------
+// This is called when an rx operation is completed. It resets the endpoint
+// vars and calls the registered completion function.
+//
+
+static void
+usbs_d12_ep_rx_complete(rx_endpoint *ep, int result)
+{
+ completion_fn fn = ep->common.complete_fn;
+ void *data = ep->common.complete_data;
+
+ usbs_d12_clear_rx_ep(ep);
+
+ if (fn)
+ (*fn)(data, result);
+}
+
+// --------------------------------------------------------------------------
+// This routine is called when an rx buffer in the chip is full and ready to
+// be read. If there's an endpoint buffer available and room to hold the data
+// we read it in, otherwise we call the completion function, but leave the
+// data in the chip. The hardware will automatically NAK packages from the
+// host until the app calls another start read to continue receiving data.
+//
+// CONTEXT:
+// Called from either the DSR or application thread, via start rx.
+// In either case, it's assumed that the chip is locked.
+//
+
+static void
+usbs_d12_ep_rx(rx_endpoint *ep)
+{
+ int n, ep_size, buf_remaining, endp = ep->endp;
+ bool done;
+
+ // The main endp is double buffered and we need to be prepared
+ // to read both simultaneously.
+ ep_size = (endp == D12_MAIN_ENDP) ? (2 * D12_MAIN_ENDP_SIZE)
+ : RX_ENDP_SIZE[endp];
+
+ buf_remaining = ep->common.buffer_size - ep->received;
+
+ // ----- If no space left in buffer, call completion fn -----
+
+ if (!ep->common.buffer || buf_remaining < ep_size) {
+ int ret = ep->received;
+
+ // See if caller requested a read smaller than the endp. Read &
+ // throw away extra
+ if (ep->common.buffer_size < ep_size) {
+ byte tmp_buf[D12_MAX_PACKET_SIZE];
+
+ if (endp == D12_MAIN_ENDP)
+ n = d12_read_main_endp_buf(D12_BASE_ADDR, tmp_buf, &done);
+ else
+ n = d12_read_endp_buf(D12_BASE_ADDR, RX_ENDP_INDEX[endp], tmp_buf);
+
+ if (n > ep->common.buffer_size) {
+ n = ep->received = ep->common.buffer_size;
+ ret = -ENOMEM;
+ TRACE_D12("\tEP%d: *** Rx Buffer too small. Data Lost ***\n", endp);
+ }
+ else
+ ret = ep->received = n;
+
+ memcpy(ep->common.buffer, tmp_buf, n);
+ buf_remaining = ep->common.buffer_size - n;
+ }
+
+ TRACE_D12("\tEP%d: Rx Complete. Buffer (nearly) full. [%d]\n",
+ endp, buf_remaining);
+ usbs_d12_ep_rx_complete(ep, ret);
+ return;
+ }
+
+ // ----- Read the data from the chip -----
+
+ if (endp == D12_MAIN_ENDP)
+ n = d12_read_main_endp_buf(D12_BASE_ADDR,
+ ep->common.buffer + ep->received, &done);
+ else {
+ n = d12_read_endp_buf(D12_BASE_ADDR, RX_ENDP_INDEX[endp],
+ ep->common.buffer + ep->received);
+ done = (n < RX_ENDP_SIZE[endp]);
+ }
+
+ ep->received += n;
+ buf_remaining = ep->common.buffer_size - ep->received;
+
+ done = done || (buf_remaining < ep_size);
+
+ TRACE_D12("EP%d: Received %d bytes.\n", endp, n);
+ TRACE_BUF("\t", ep->common.buffer + ep->received-n, n);
+
+ // ----- If we're done, complete the receive -----
+
+ if (done) {
+ TRACE_D12("\tEP%d Rx Complete (%d) %p\n", endp,
+ ep->received, ep->common.complete_fn);
+ usbs_d12_ep_rx_complete(ep, ep->received);
+ }
+}
+
+// --------------------------------------------------------------------------
+// Stalls/unstalls the specified endpoint.
+
+static void
+usbs_d12_stall_rx_ep(rx_endpoint *ep, cyg_bool halt)
+{
+ ep->common.halted = halt;
+ d12_stall_endp(D12_BASE_ADDR, RX_ENDP_INDEX[ep->endp], halt);
+}
+
+// --------------------------------------------------------------------------
+// Handler for an Rx endpoint full interrupt. It clears the interrupt on the
+// D12 by reading the endpoint's status register then calls the routine to
+// read the data into the buffer.
+//
+// Called from the DSR context only.
+//
+
+static void
+usbs_d12_ep_rx_intr(rx_endpoint *ep)
+{
+ d12_read_last_trans_status(D12_BASE_ADDR, RX_ENDP_INDEX[ep->endp]);
+ usbs_d12_ep_rx(ep);
+}
+
+#endif
+
+// --------------------------------------------------------------------------
+// Common Tx Endpoint 1 & 2
+// --------------------------------------------------------------------------
+
+#if defined(_TX_EP1) || defined(_TX_EP2)
+
+// Clears out the endpoint data structure before/after a tx is complete.
+
+static void usbs_d12_clear_tx_ep(tx_endpoint *ep)
+{
+ ep->common.buffer = 0;
+ ep->common.buffer_size = 0;
+ ep->common.complete_fn = 0;
+ ep->common.complete_data = 0;
+
+ ep->transmitted = 0;
+ ep->tx_empty = false;
+}
+
+// --------------------------------------------------------------------------
+// This is called when a transmit is completed. It resets the endpoint vars
+// and calls the registered completion function, if any.
+//
+// CONTEXT:
+// Called from either the DSR or the app thread that started tx.
+
+static void usbs_d12_ep_tx_complete(tx_endpoint *ep, int result)
+{
+ completion_fn fn = ep->common.complete_fn;
+ void *data = ep->common.complete_data;
+
+ usbs_d12_clear_tx_ep(ep);
+
+ if (fn)
+ (*fn)(data, result);
+}
+
+// --------------------------------------------------------------------------
+// The routine writes data to the chip and updates the endpoint's counters.
+// It gets called at the start of a transfer operation to prime the device
+// and then gets called each time the chip finishes sending a packet to the
+// host and is ready for more data. If the amount of data remaining is
+// smaller than can fit in the chip's endpoint buffer, then this is the last
+// packet to send, so we call the completion function.
+//
+// CONTEXT:
+// Called from either the DSR or the app thread that started the tx
+// In either case, it's assumed the chip is locked.
+
+static void
+usbs_d12_ep_tx(tx_endpoint *ep)
+{
+ int n, nRemaining;
+
+ // ----- Already done. Intermittent interrupt, so get out -----
+
+ if (!ep->common.buffer)
+ return;
+
+ // ----- See how many bytes remaining in buffer -----
+
+ nRemaining = ep->common.buffer_size - ep->transmitted;
+
+ TRACE_D12("EP%d: Tx %p, %d Done, %d Remaining\n", ep->endp,
+ ep->common.buffer, ep->transmitted, nRemaining);
+
+ // ----- If prev packet was last, signal that we're done -----
+
+ if (nRemaining == 0 && !ep->tx_empty) {
+ TRACE_D12("\tEP%d: Tx complete (%d) %p\n", ep->endp,
+ ep->transmitted, ep->common.complete_fn);
+ usbs_d12_ep_tx_complete(ep, ep->transmitted);
+ return;
+ }
+
+ // ----- Write the next packet to chip -----
+
+ if (nRemaining < TX_ENDP_SIZE[ep->endp]) {
+ n = nRemaining;
+ ep->tx_empty = false;
+ }
+ else
+ n = TX_ENDP_SIZE[ep->endp];
+
+ TRACE_D12("EP%d: Writing %d bytes. %s\n", ep->endp,
+ n, (n == 0) ? "DONE" : "");
+ TRACE_BUF("\t", ep->common.buffer + ep->transmitted, n);
+
+ d12_write_endp_buf(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp],
+ ep->common.buffer + ep->transmitted, (uint8) n);
+
+ ep->transmitted += n;
+
+ // ----- If empty packet, complete now -----
+
+ if (n == 0) {
+ TRACE_D12("\tEP%d: Tx complete (%d) %p\n", ep->endp,
+ ep->transmitted, ep->common.complete_fn);
+ usbs_d12_ep_tx_complete(ep, ep->transmitted);
+ return;
+ }
+}
+
+// --------------------------------------------------------------------------
+// Stalls/unstalls the specified tx endpoint.
+
+static void
+usbs_d12_stall_tx_ep(tx_endpoint *ep, cyg_bool halt)
+{
+ ep->common.halted = halt;
+ d12_stall_endp(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp], halt);
+}
+
+// --------------------------------------------------------------------------
+// Handler for when the chip's tx RAM for an endoint has just been emptied
+// (sent to the host) and the chip is ready for more data.
+// We read the endpoint's last trans status register to clear the interrupt
+// on the D12, then call the tx function to send the next packet or
+// complete the transfer.
+
+static void
+usbs_d12_ep_tx_intr(tx_endpoint *ep)
+{
+ d12_read_last_trans_status(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp]);
+ usbs_d12_ep_tx(ep);
+}
+
+#endif // defined(_TX_EP1) || defined(_TX_EP2)
+
+// --------------------------------------------------------------------------
+// Application Program Interface (API)
+// --------------------------------------------------------------------------
+
+#if defined(_RX_EP1) || defined(_RX_EP2)
+// Starts a receive operation on the specified endpoint. If the buffer size
+// is zero the completion function is called immediately. The routine checks
+// if tehre is data in the chip's endpoint buffer, and if so it will call
+// ep_rx() to start reading the data out of the chip.
+//
+// If the endpoint is currently stalled, a read size of zero can be used to
+// block the calling thread until the stall is cleared. If the read size is
+// non-zero and the endpoint is stalled the completion function is called
+// immediately with an error result.
+
+static void
+usbs_d12_api_start_rx_ep(usbs_rx_endpoint *ep)
+{
+ rx_endpoint *epx = (rx_endpoint *) ep;
+
+ if (ep->halted) {
+ if (ep->buffer_size != 0)
+ usbs_d12_ep_rx_complete(epx, -EAGAIN);
+ }
+ else if (ep->buffer_size == 0) {
+ usbs_d12_ep_rx_complete(epx, 0);
+ }
+ else {
+ TRACE_D12("EP%d: Starting Rx, %p, %d\n", epx->endp, ep->buffer,
+ ep->buffer_size);
+ usbs_d12_lock();
+
+ epx->received = 0;
+ if (d12_data_available(D12_BASE_ADDR, RX_ENDP_INDEX[epx->endp]))
+ usbs_d12_ep_rx(epx);
+
+ usbs_d12_unlock();
+ }
+}
+
+// --------------------------------------------------------------------------
+// Halts/unhalts one of the generic rx (OUT) endpoints.
+//
+
+static void usbs_d12_api_stall_rx_ep(usbs_rx_endpoint *ep, cyg_bool halt)
+{
+ usbs_d12_lock();
+ usbs_d12_stall_rx_ep((rx_endpoint*) ep, halt);
+ usbs_d12_unlock();
+}
+
+#endif // defined(_RX_EP1) || defined(_RX_EP2)
+
+// --------------------------------------------------------------------------
+// Tx API
+// --------------------------------------------------------------------------
+
+#if defined(_TX_EP1) || defined(_TX_EP2)
+
+// This starts a transmit on one of the data endpoints. If the endpoint is
+// stalled a buffer size of zero can be used to block until the stall is
+// cleared. Any other size on a stalled endpoint will result in an error
+// callback immediately. The first packet is sent to the chip immediately,
+// in the application context. If the chip's buffer can contain the whole
+// transfer, the completion function will be called immediately, again,
+// still in the application context.
+//
+// If an empty packet is requested we send one from here and call the
+// completion function. This should not cause an intr on the D12.
+//
+// CONTEXT:
+// Called from an application thread
+
+static void usbs_d12_api_start_tx_ep(usbs_tx_endpoint *ep)
+{
+ tx_endpoint *epx = (tx_endpoint*) ep;
+
+ if (ep->halted) {
+ if (ep->buffer_size != 0)
+ usbs_d12_ep_tx_complete(epx, -EAGAIN);
+ }
+ else if (ep->buffer_size == 0) {
+ usbs_d12_lock();
+
+ d12_write_endp_buf(D12_BASE_ADDR, TX_ENDP_INDEX[epx->endp], 0, 0);
+ usbs_d12_ep_tx_complete(epx, 0);
+
+ usbs_d12_unlock();
+ }
+ else {
+ TRACE_D12("EP%d: Starting Tx, %p, %d\n", epx->endp, ep->buffer,
+ ep->buffer_size);
+ usbs_d12_lock();
+
+ epx->tx_empty = true;
+ epx->transmitted = 0;
+ usbs_d12_ep_tx(epx);
+
+ usbs_d12_unlock();
+ }
+}
+
+// --------------------------------------------------------------------------
+// Halts/unhalts one of the generic endpoints.
+
+static void
+usbs_d12_api_stall_tx_ep(usbs_tx_endpoint *ep, cyg_bool halt)
+{
+ usbs_d12_lock();
+ usbs_d12_stall_tx_ep((tx_endpoint*) ep, halt);
+ usbs_d12_unlock();
+}
+
+#endif // defined(_TX_ENDP1) || defined(_TX_EP2)
+
+// --------------------------------------------------------------------------
+// DSR
+// --------------------------------------------------------------------------
+
+// The DSR for the D12 chip. This is normally called in the DSR context when
+// the D12 has raised its interrupt flag indicating that it needs to be
+// serviced. The interrupt register contains bit flags that are OR'ed togther
+// indicating what items need to be serviced. There are flags for the
+// following:
+// - The endpoints (one bit for each)
+// - Bus Reset
+// - Suspend Change
+// - DMA (terminal count)
+//
+// Care must be taken in that the D12's interrupt output is level-sensitive
+// (in that the interrupt sources are OR'ed together and not all cleared
+// atomically in a single operation). Platforms (such as the PC) may be
+// expecting edge-triggered interrupts, so we must work around that.
+// So, we loop on the interrupt register. Even though, in each loop, we
+// perform all of the required operations to clear the interrupts, a new
+// one may have arrived before we finished clearing the previous ones.
+// So we read the intr reg again. Once the intr reg gives a zero reading
+// we know that the D12 has dropped its IRQ line.
+//
+// Note, if we're configured to use a thread, this routine is called from
+// within a thread context (not a DSR context).
+//
+
+static void
+usbs_d12_dsr(cyg_vector_t vector, cyg_ucount32 count,
+ cyg_addrword_t data)
+{
+ uint16 status;
+ bool suspended;
+
+ CYG_ASSERT(vector == CYGNUM_DEVS_USB_D12_INT,
+ "DSR should only be invoked for D12 interrupts");
+
+ while ((status = d12_read_intr_reg(D12_BASE_ADDR)) != 0) {
+ TRACE_D12("Intr Status: 0x%04X\n", (unsigned) status);
+
+ if (status & D12_INTR_BUS_RESET) {
+ TRACE_D12("\n>>> Bus Reset <<<\n");
+ usbs_d12_reset();
+ }
+ else {
+
+ // ----- Suspend Change -----
+
+ suspended = (bool) (ep0.common.state & USBS_STATE_SUSPENDED);
+
+ if (status & D12_INTR_SUSPEND_CHANGE) {
+ if (!suspended && (status & ~D12_INTR_SUSPEND_CHANGE) == 0)
+ usbs_d12_suspend(true);
+ }
+ else if (suspended)
+ usbs_d12_suspend(false);
+
+ // ----- Bulk Endpoints -----
+
+#ifdef _TX_EP2
+ if (status & D12_INTR_TX_ENDP2)
+ usbs_d12_ep_tx_intr(&tx_ep2);
+#endif
+
+#ifdef _RX_EP2
+ if (status & D12_INTR_RX_ENDP2)
+ usbs_d12_ep_rx_intr(&rx_ep2);
+#endif
+
+ // ----- Interrupt Endpoints -----
+
+#ifdef _TX_EP1
+ if (status & D12_INTR_TX_ENDP1)
+ usbs_d12_ep_tx_intr(&tx_ep1);
+#endif
+
+#ifdef _RX_EP1
+ if (status & D12_INTR_RX_ENDP1)
+ usbs_d12_ep_rx_intr(&rx_ep1);
+#endif
+
+ // ----- Control Endpoint -----
+
+ if (status & D12_INTR_TX_CTRL_ENDP)
+ usbs_d12_ep0_tx_intr();
+
+ if (status & D12_INTR_RX_CTRL_ENDP)
+ usbs_d12_ep0_rx_intr();
+ }
+ }
+
+ cyg_drv_interrupt_unmask(vector);
+}
+
+// --------------------------------------------------------------------------
+// Interrupt
+// --------------------------------------------------------------------------
+
+// Here, the ISR does nothing but schedule the DSR to run. The ISR's/DSR's
+// are serialized. The CPU won't process another ISR until after the DSR
+// completes.
+
+static uint32
+usbs_d12_isr(cyg_vector_t vector, cyg_addrword_t data)
+{
+ CYG_ASSERT(CYGNUM_DEVS_USB_D12_INT == vector,
+ "usbs_isr: Incorrect interrupt");
+
+ // Prevent another interrupt until DSR completes
+ cyg_drv_interrupt_mask(vector);
+ cyg_drv_interrupt_acknowledge(vector);
+
+ return CYG_ISR_HANDLED | CYG_ISR_CALL_DSR;
+}
+
+// --------------------------------------------------------------------------
+// Polling
+// --------------------------------------------------------------------------
+
+static void
+usbs_d12_poll(usbs_control_endpoint *endp)
+{
+ CYG_ASSERT(endp == &ep0.common, "usbs_poll: wrong endpoint");
+
+ usbs_d12_lock();
+ usbs_d12_dsr(CYGNUM_DEVS_USB_D12_INT, 0, 0);
+ usbs_d12_unlock();
+}
+
+// --------------------------------------------------------------------------
+// Thread Processing
+// --------------------------------------------------------------------------
+
+// The user can opt to configure the driver to service the D12 using a high
+// priority thread. The thread's priority MUST be greater than the priority
+// of any application thread making calls into the driver.
+// When we use a thread, the DSR simply signals a semaphore tio wake the
+// thread up. The thread, in turn, calls the the routine to service the D12,
+// now in a thread context. This allows for greater debug options, including
+// tracing.
+
+#ifdef CYGPKG_DEVS_USB_D12_THREAD
+
+static byte usbs_d12_thread_stack[CYGNUM_DEVS_USB_D12_THREAD_STACK_SIZE];
+static cyg_thread usbs_d12_thread;
+static cyg_handle_t usbs_d12_thread_handle;
+static cyg_sem_t usbs_d12_sem;
+
+static void
+usbs_d12_thread_dsr(cyg_vector_t vector, cyg_ucount32 count,
+ cyg_addrword_t data)
+{
+ cyg_semaphore_post(&usbs_d12_sem);
+
+ CYG_UNUSED_PARAM(cyg_vector_t, vector);
+ CYG_UNUSED_PARAM(cyg_ucount32, count);
+ CYG_UNUSED_PARAM(cyg_addrword_t, data);
+}
+
+
+static void
+usbs_d12_thread_fn(cyg_addrword_t param)
+{
+ while (1) {
+ cyg_semaphore_wait(&usbs_d12_sem);
+ usbs_d12_poll(&ep0.common);
+ }
+
+ CYG_UNUSED_PARAM(cyg_addrword_t, param);
+}
+
+
+static void
+usbs_d12_thread_init(void)
+{
+ cyg_semaphore_init(&usbs_d12_sem, 0);
+
+ cyg_thread_create(CYGNUM_DEVS_USB_D12_THREAD_PRIORITY,
+ &usbs_d12_thread_fn, 0, "D12 USB Driver Thread",
+ usbs_d12_thread_stack,
+ CYGNUM_DEVS_USB_D12_THREAD_STACK_SIZE,
+ &usbs_d12_thread_handle, &usbs_d12_thread);
+ cyg_thread_resume(usbs_d12_thread_handle);
+}
+
+#endif // CYGPKG_DEVS_USB_D12_THREAD
+
+// --------------------------------------------------------------------------
+// Start/Reset
+// --------------------------------------------------------------------------
+
+// Chip initialization and handler for a USB Bus Reset. This gets called at
+// system startup and after a USB Bus Reset. It puts the chip into the
+// default state, with USB Address 0, connected to the bus (i.e.
+// "SoftConnect" asserted). Interrupts to the main endpoint are turned on
+// via the DMA register.
+
+static void
+usbs_d12_reset(void)
+{
+ int old_state = ep0.common.state;
+ ep0.common.state = USBS_STATE_DEFAULT;
+
+ if (ep0.common.state_change_fn) {
+ (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+ USBS_STATE_CHANGE_RESET, old_state);
+ }
+
+ d12_set_addr_enable(D12_BASE_ADDR, 0, true);
+ d12_set_endp_enable(D12_BASE_ADDR, true);
+ d12_set_dma(D12_BASE_ADDR, D12_DMA_MAIN_ENDP_INTR_ENABLE);
+ d12_set_mode(D12_BASE_ADDR, D12_MODE_CFG_DFLT | D12_MODE_CFG_SOFT_CONNECT,
+ D12_MODE_CLK_DFLT);
+
+ // If any endpoints were going, signal the end of transfers
+
+#if defined(_TX_EP2)
+ usbs_d12_ep_tx_complete(&tx_ep2, -EPIPE);
+#endif
+
+#if defined(_RX_EP2)
+ usbs_d12_ep_rx_complete(&rx_ep2, -EPIPE);
+#endif
+
+#if defined(_TX_EP1)
+ usbs_d12_ep_tx_complete(&tx_ep1, -EPIPE);
+#endif
+
+#if defined(_RX_EP1)
+ usbs_d12_ep_rx_complete(&rx_ep1, -EPIPE);
+#endif
+}
+
+// --------------------------------------------------------------------------
+// The start function is called indirectly by the application when
+// initialization is complete. By this time, the enumeration data has been
+// assigned to the control endpoint and we're ready to connect to the host.
+// Within the reset function the D12's SoftConnect line is asserted which
+// allows the host (hub) to see us on the USB bus. If connected, the host
+// should start the enumeration process.
+//
+
+static void usbs_d12_start(usbs_control_endpoint *endpoint)
+{
+#if defined(_TRACE) && !defined(_TRACE_STDOUT)
+ TRACE_OPEN(TRACE_SINK);
+#endif
+
+ CYG_ASSERT(endpoint == &ep0.common, "ep0 start: wrong endpoint");
+ TRACE_D12("USBS D12: Starting.\n");
+
+ d12_clear_all_intr(D12_BASE_ADDR);
+ usbs_d12_reset();
+}
+
+// --------------------------------------------------------------------------
+// Initialization
+// --------------------------------------------------------------------------
+
+// This routine is called early in the program's startup, possibly before
+// main (from within a C++ object initialization). We want to put this chip
+// and driver in a neutral, but ready, state until the application gets
+// control, initializes itself and calls the usb start function.
+//
+// The D12 has a "Soft Connect" feature to tristate the USB bus, making it
+// appear that the USB device is not connected to the bus. We initially
+// keep seperated from the bus to allow for initialization.
+
+void
+usbs_d12_init(void)
+{
+ cyg_DSR_t *pdsr;
+
+ d12_set_mode(D12_BASE_ADDR, D12_MODE_CFG_DFLT & ~D12_MODE_CFG_SOFT_CONNECT,
+ D12_MODE_CLK_DFLT);
+
+ d12_set_addr_enable(D12_BASE_ADDR, 0, false);
+ d12_set_endp_enable(D12_BASE_ADDR, false);
+
+ // ----- Clear the endpoints -----
+
+#if defined(_TX_EP2)
+ usbs_d12_clear_tx_ep(&tx_ep2);
+#endif
+
+#if defined(_RX_EP2)
+ usbs_d12_clear_rx_ep(&rx_ep2);
+#endif
+
+#if defined(_TX_EP1)
+ usbs_d12_clear_tx_ep(&tx_ep1);
+#endif
+
+#if defined(_RX_EP1)
+ usbs_d12_clear_rx_ep(&rx_ep1);
+#endif
+
+ // ----- Start the thread (if we're using it) -----
+
+#ifdef CYGPKG_DEVS_USB_D12_THREAD
+ usbs_d12_thread_init();
+ pdsr = &usbs_d12_thread_dsr;
+#else
+ pdsr = &usbs_d12_dsr;
+#endif
+
+ // ----- Attach the ISR -----
+
+ cyg_drv_interrupt_create(CYGNUM_DEVS_USB_D12_INT,
+ 0, 0, &usbs_d12_isr, pdsr,
+ &usbs_d12_intr_handle, &usbs_d12_intr_data);
+
+ cyg_drv_interrupt_attach(usbs_d12_intr_handle);
+ cyg_drv_interrupt_unmask(CYGNUM_DEVS_USB_D12_INT);
+}
+
+// ----------------------------------------------------------------------------
+// Testing support.
+
+usbs_testing_endpoint usbs_testing_endpoints[] = {
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL,
+ endpoint_number : 0,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &ep0.common,
+#ifdef CYGVAR_DEVS_USB_D12_EP0_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "0c",
+#else
+ devtab_entry : (const char*) 0,
+#endif
+ min_size : 1,
+ max_size : CYGNUM_DEVS_USB_D12_EP0_TXBUFSIZE,
+ max_in_padding : 0,
+ alignment : 0
+ },
+
+ /*
+#ifdef _TX_EP1
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT,
+ endpoint_number : 1,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &tx_ep1.common,
+# ifdef CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "1w",
+# else
+ devtab_entry : (const char*) 0,
+# endif
+ min_size : 0,
+ max_size : 0x0FFFF, // Driver limitation, only a single
+ // buffer descriptor is used
+ max_in_padding : 0,
+ alignment : HAL_DCACHE_LINE_SIZE
+ },
+#endif
+
+#ifdef _RX_EP1
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT,
+ endpoint_number : 1,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT,
+ endpoint : (void*) &rx_ep1.common,
+# ifdef CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "1r",
+# else
+ devtab_entry : (const char*) 0,
+# endif
+ min_size : 1,
+ max_size : 0x0FFFF, // Driver limitation
+ max_in_padding : 0,
+ alignment : HAL_DCACHE_LINE_SIZE
+ },
+#endif
+ */
+
+#ifdef _TX_EP2
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+ endpoint_number : 2,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &tx_ep2.common,
+# ifdef CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "2w",
+# else
+ devtab_entry : (const char*) 0,
+# endif
+ min_size : 0,
+ max_size : 0x1000, // 4k for testing
+ max_in_padding : 0,
+ alignment : HAL_DCACHE_LINE_SIZE
+ },
+#endif
+
+#ifdef _RX_EP2
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+ endpoint_number : 2,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT,
+ endpoint : (void*) &rx_ep2.common,
+# ifdef CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "2r",
+# else
+ devtab_entry : (const char*) 0,
+# endif
+ min_size : 1,
+ max_size : 0x1000, // 4k for testing
+ max_in_padding : 0,
+ alignment : HAL_DCACHE_LINE_SIZE
+ },
+#endif
+
+ USBS_TESTING_ENDPOINTS_TERMINATOR
+};
+
diff --git a/ecos/packages/devs/usb/d12/current/src/usbs_d12_data.cxx b/ecos/packages/devs/usb/d12/current/src/usbs_d12_data.cxx
new file mode 100644
index 0000000..4fef23e
--- /dev/null
+++ b/ecos/packages/devs/usb/d12/current/src/usbs_d12_data.cxx
@@ -0,0 +1,231 @@
+//==========================================================================
+//
+// usbs_d12_data.cxx
+//
+// Static data for the D12 USB device driver
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006 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): fmp
+// Contributors: fmp
+// Date: 2004-05-27
+//
+// This file contains various objects that should go into extras.o
+// rather than libtarget.a, e.g. devtab entries that would normally
+// be eliminated by the selective linking.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/io/devtab.h>
+#include <cyg/io/usb/usbs_d12.h>
+#include <pkgconf/devs_usb_d12.h>
+
+// ----------------------------------------------------------------------------
+// Initialization. The goal here is to call usbs_d12_init()
+// early on during system startup, to take care of things like
+// registering interrupt handlers etc. which are best done
+// during system init.
+//
+// If the endpoint 0 devtab entry is available then its init()
+// function can be used to take care of this. However the devtab
+// entries are optional so an alternative mechanism must be
+// provided. Unfortunately although it is possible to give
+// a C function the constructor attribute, it cannot be given
+// an initpri attribute. Instead it is necessary to define a
+// dummy C++ class.
+
+extern "C" void usbs_d12_init(void);
+
+#ifndef CYGVAR_DEVS_USB_D12_EP0_DEVTAB_ENTRY
+class usbs_d12_initialization {
+public:
+ usbs_d12_initialization() {
+ usbs_d12_init();
+ }
+};
+
+static usbs_d12_initialization usbs_d12_init_object
+ CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_IO);
+#endif
+
+// ----------------------------------------------------------------------------
+// The devtab entries. Each of these is optional, many applications
+// will want to use the lower-level API rather than go via
+// open/read/write/ioctl.
+
+#ifdef CYGVAR_DEVS_USB_D12_EP0_DEVTAB_ENTRY
+
+// For endpoint 0 the only legal operations are get_config() and
+// set_config(), and these are provided by the common package.
+
+static bool
+usbs_d12_devtab_ep0_init(struct cyg_devtab_entry* tab)
+{
+ CYG_UNUSED_PARAM(struct cyg_devtab_entry*, tab);
+ usbs_d12_init();
+ return true;
+}
+
+static
+CHAR_DEVIO_TABLE(usbs_d12_ep0_devtab_functions,
+ &cyg_devio_cwrite,
+ &cyg_devio_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+static CHAR_DEVTAB_ENTRY(usbs_d12_ep0_devtab_entry,
+ CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "0c",
+ 0,
+ &usbs_d12_ep0_devtab_functions,
+ &usbs_d12_devtab_ep0_init,
+ 0,
+ (void*) &usbs_d12_ep0);
+#endif
+
+// ----------------------------------------------------------------------------
+// Common routines for ep1 and ep2.
+
+#if defined(CYGVAR_DEVS_USB_D12_TX_EP1_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_D12_RX_EP1_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY)
+
+static bool
+usbs_d12_devtab_dummy_init(struct cyg_devtab_entry* tab)
+{
+ CYG_UNUSED_PARAM(struct cyg_devtab_entry*, tab);
+ return true;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// tx (in) ep1 devtab entry. This can only be used for slave->host,
+// so only the cwrite() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_D12_TX_EP1_DEVTAB_ENTRY
+
+static
+CHAR_DEVIO_TABLE(usbs_d12_tx_ep1_devtab_functions,
+ &usbs_devtab_cwrite,
+ &cyg_devio_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+static
+CHAR_DEVTAB_ENTRY(usbs_d12_tx_ep1_devtab_entry,
+ CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "1w",
+ 0,
+ &usbs_d12_tx_ep1_devtab_functions,
+ &usbs_d12_devtab_dummy_init,
+ 0,
+ (void*) &usbs_d12_tx_ep1);
+#endif
+
+// ----------------------------------------------------------------------------
+// rx (out) ep1 devtab entry. This can only be used for host->slave,
+// so only the cread() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_D12_RX_EP1_DEVTAB_ENTRY
+
+static
+CHAR_DEVIO_TABLE(usbs_d12_rx_ep1_devtab_functions,
+ &cyg_devio_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+static
+CHAR_DEVTAB_ENTRY(usbs_d12_rx_ep1_devtab_entry,
+ CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "1r",
+ 0,
+ &usbs_d12_rx_ep1_devtab_functions,
+ &usbs_d12_devtab_dummy_init,
+ 0,
+ (void*) &usbs_d12_rx_ep1);
+#endif
+
+
+// ----------------------------------------------------------------------------
+// tx (in) ep2 devtab entry. This can only be used for slave->host, so only the
+// cwrite() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_D12_TX_EP2_DEVTAB_ENTRY
+
+static
+CHAR_DEVIO_TABLE(usbs_d12_tx_ep2_devtab_functions,
+ &usbs_devtab_cwrite,
+ &cyg_devio_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+static
+CHAR_DEVTAB_ENTRY(usbs_d12_tx_ep2_devtab_entry,
+ CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "2w",
+ 0,
+ &usbs_d12_tx_ep2_devtab_functions,
+ &usbs_d12_devtab_dummy_init,
+ 0,
+ (void*) &usbs_d12_tx_ep2);
+#endif
+
+// ----------------------------------------------------------------------------
+// rx (out) ep2 devtab entry. This can only be used for host->slave,
+// so only the cread() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_D12_RX_EP2_DEVTAB_ENTRY
+
+static
+CHAR_DEVIO_TABLE(usbs_d12_rx_ep2_devtab_functions,
+ &cyg_devio_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+static
+CHAR_DEVTAB_ENTRY(usbs_d12_rx_ep2_devtab_entry,
+ CYGDAT_DEVS_USB_D12_DEVTAB_BASENAME "2r",
+ 0,
+ &usbs_d12_rx_ep2_devtab_functions,
+ &usbs_d12_devtab_dummy_init,
+ 0,
+ (void*) &usbs_d12_rx_ep2);
+#endif
diff --git a/ecos/packages/devs/usb/i386/SoRoD12/current/ChangeLog b/ecos/packages/devs/usb/i386/SoRoD12/current/ChangeLog
new file mode 100644
index 0000000..62c4456
--- /dev/null
+++ b/ecos/packages/devs/usb/i386/SoRoD12/current/ChangeLog
@@ -0,0 +1,28 @@
+2006-06-06 Frank Pagliughi <fpagliughi@mindspring.com>
+
+ * First version of the USB device driver using the philips D12
+ for i386 targets.
+
+//===========================================================================
+// ####GPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2006 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####
+//===========================================================================
diff --git a/ecos/packages/devs/usb/i386/SoRoD12/current/cdl/usbs_i386_sorod12.cdl b/ecos/packages/devs/usb/i386/SoRoD12/current/cdl/usbs_i386_sorod12.cdl
new file mode 100644
index 0000000..4e76de2
--- /dev/null
+++ b/ecos/packages/devs/usb/i386/SoRoD12/current/cdl/usbs_i386_sorod12.cdl
@@ -0,0 +1,92 @@
+# ====================================================================
+#
+# usbs_i386_sorod12.cdl
+#
+# Hardware specific parts for the SoRo D12 PC 104 USB card.
+#
+# ====================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 2003, 2004, 2006 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): Frank M. Pagliughi (fmp), SoRo Systems, Inc., asl
+# Contributors:
+# Date: 2006-04-27
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_USB_I386_SOROD12 {
+ display "Hardware specific part for SoRo D12 USB Device Driver"
+ include_dir "cyg/io/usb"
+ parent CYGPKG_DEVS_USB_D12
+
+ requires { CYGIMP_DEVS_USB_D12_HW_ACCESS_HEADER ==
+ "<cyg/io/usb/usbs_i386_sorod12.inl>" }
+
+ cdl_option CYGSEM_DEVS_USB_I386_SOROD12_IO_MAPPED {
+ display "I/O mapped."
+ flavor bool
+ default_value 1
+ description "
+ The PDIUSBD12 can be mapped into the processor's I/O space or memory
+ space. If this is set the driver accesses the chip through HAL_READ
+ and HAL_WRITE macros, otherwise it uses direct memory access."
+ }
+
+ cdl_option CYGNUM_DEVS_USB_I386_SOROD12_BASEADDR {
+ display "Base Address of D12 chip"
+ flavor data
+ no_define
+ legal_values 1 to 0xFF8
+ default_value 544
+ active_if CYGFUN_DEVS_USB_D12_EP0
+ requires { CYGNUM_DEVS_USB_D12_BASEADDR ==
+ CYGNUM_DEVS_USB_I386_SOROD12_BASEADDR }
+ description "
+ The base memory or I/O address where the USB chip resides."
+ }
+
+ cdl_option CYGNUM_DEVS_USB_I386_SORODD12_IRQ {
+ display "IRQ for the D12 chip"
+ flavor data
+ no_define
+ legal_values { 3 5 7 }
+ default_value 5
+ active_if CYGFUN_DEVS_USB_D12_EP0
+ requires { CYGNUM_DEVS_USB_D12_IRQ ==
+ CYGNUM_DEVS_USB_I386_SORODD12_IRQ }
+ description "
+ The IRQ assigned to the D12 chip."
+ }
+}
diff --git a/ecos/packages/devs/usb/i386/SoRoD12/current/include/usbs_i386_sorod12.inl b/ecos/packages/devs/usb/i386/SoRoD12/current/include/usbs_i386_sorod12.inl
new file mode 100644
index 0000000..d74883b
--- /dev/null
+++ b/ecos/packages/devs/usb/i386/SoRoD12/current/include/usbs_i386_sorod12.inl
@@ -0,0 +1,130 @@
+#ifndef USBS_I386_SORO12_INL
+#define USBS_I386_SORO12_INL
+//==========================================================================
+//
+// usbS_i386_sorod12.inl
+//
+// Hardware specific parts for the SoRo D12 PC 104 USB card.
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 2006 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): Frank M. Pagliughi (fmp), ASL
+// Date: 2004-05-22
+//
+// This code is a hardware specific device driver for the SoRo Systems
+// USB-D12-104, a PC/104 (ISA) Full-Speed USB slave board, which turns
+// a PC/104 stack into a USB slave device. The board contains a
+// Philips PDIUSBD12 Peripheral Controller Chip mapped into the PC's
+// I/O space, with jumper-selectable I/O base address, IRQ, and DMA
+// settings. The eCos config tool is used to adjust settings for this
+// driver to match the physical jumper settings. The chip could run in
+// polled mode without an IRQ, but this wouldn't be a great idea other
+// than maybe a debug environment.
+
+// ------------------------------------------------------------------------
+// Data-Only I/O
+// ------------------------------------------------------------------------
+//
+
+#include <pkgconf/devs_usb_i386_sorod12.h>
+
+// These routines read or write 8 bit values to the data area.
+
+static inline byte d12_read_data_byte(d12_addr_type base_addr)
+{
+ #if defined(CYGSEM_DEVS_USB_I386_SOROD12_IO_MAPPED)
+ byte val;
+ HAL_READ_UINT8((unsigned) base_addr, val);
+ return val;
+ #else
+ return *base_addr;
+ #endif
+}
+
+static inline void d12_write_data_byte(d12_addr_type base_addr, byte val)
+{
+ #if defined(CYGSEM_DEVS_USB_I386_SOROD12_IO_MAPPED)
+ HAL_WRITE_UINT8((unsigned) base_addr, val);
+ #else
+ *base_addr = val;
+ #endif
+}
+
+// This routine writes a command to the device.
+
+static inline void d12_write_cmd(d12_addr_type base_addr, byte cmd)
+{
+ #if defined(CYGSEM_DEVS_USB_I386_SOROD12_IO_MAPPED)
+ HAL_WRITE_UINT8((unsigned) base_addr+1, cmd);
+ #else
+ *(base_addr+1) = cmd;
+ #endif
+}
+
+// ------------------------------------------------------------------------
+// Reads 'n' bytes from the data address of the D12 and places them into
+// the buf[] array. Buf can be NULL, in which case the specified number of
+// bytes are read and discarded.
+
+static uint8 d12_read_data(d12_addr_type base_addr, byte *buf, uint8 n)
+{
+ uint8 i;
+
+ if (buf) {
+ for (i=0; i<n; ++i)
+ buf[i] = d12_read_data_byte(base_addr);
+ }
+ else {
+ for (i=0; i<n; ++i)
+ d12_read_data_byte(base_addr);
+ n = 0;
+ }
+
+ return n;
+}
+
+// ------------------------------------------------------------------------
+// Writes 'n' bytes out the data reg of the chip
+
+static void d12_write_data(d12_addr_type base_addr, const byte *buf, uint8 n)
+{
+ uint8 i;
+
+ for (i=0; i<n; ++i)
+ d12_write_data_byte(base_addr, buf[i]);
+}
+
+#endif // USBS_I386_SORO12_INL
diff --git a/ecos/packages/devs/usb/nec_upd985xx/current/ChangeLog b/ecos/packages/devs/usb/nec_upd985xx/current/ChangeLog
new file mode 100644
index 0000000..8235811
--- /dev/null
+++ b/ecos/packages/devs/usb/nec_upd985xx/current/ChangeLog
@@ -0,0 +1,125 @@
+2003-02-25 Jonathan Larmour <jifl@eCosCentric.com>
+
+ * doc/usbs_upd985xx.sgml: Declare as <part> not <reference> to get
+ correct TOC numbering.
+
+2003-02-24 Jonathan Larmour <jifl@eCosCentric.com>
+
+ * cdl/usbs_upd985xx.cdl: Fix doc link.
+
+ * doc/usbs_upd985xx.sgml: Comment out DOCTYPE for now to allow building
+ with standard doc build.
+ Add an enclosing <reference> so it's structured better with standard
+ doc build.
+
+2003-01-22 Anssi Pulkkinen <anssi.pulkkinen@ascom.ch>
+
+ * src/usbs_upd985xx.c (ep0_rx_dsr): After sending a stall response
+ on ep0, start a new receive process for the next control message.
+
+2002-12-01 Bart Veer <bartv@ecoscentric.com>
+
+ * src/usbs_upd985xx.c, cdl/usbs_upd985xx.cdl:
+ Make the control packet size configurable, to work around a
+ problem detected by USB compliance testing. Based on work
+ by Clark Williams and Andrew Lunn.
+
+2002-10-26 Bart Veer <bartv@ecoscentric.com>
+
+ * src/usbs_upd985xx.c (ep0_rx_dsr):
+ Fix typo in expression, reported by Andrew Lunn. The system's
+ behaviour should not be affected.
+
+2001-09-20 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_upd985xx.c (ep0_init):
+ During a reset, also reset the tx_in_progress and
+ tx_pending locks. Otherwise if there are ongoing
+ transmits while the host is issuing a reset no
+ further transmits would be possible.
+
+2001-09-14 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_upd985xx.c:
+ Various changes related to USB testing. Also include
+ potential work-arounds for some unconfirmed hardware
+ problems, but these are disabled for now.
+
+ * src/usbs_upd985xx.c:
+ Change how the reset signal interrupt bit gets masked, to
+ cope with the way Windows initializes a new USB device.
+
+2001-08-09 Bart Veer <bartv@redhat.com>
+
+ * doc/usbs_upd985xx.sgml, doc/*.html:
+ Updated to describe the driver's current behaviour.
+
+ * src/usbs_upd985xx.c, cdl/usbs_upd985xx.cdl:
+ Implement workaround for some hardware problems,
+ by serializing transmit operations.
+
+2001-08-08 Bart Veer <bartv@redhat.com>
+
+ * cdl/usbs_upd985xx.cdl, include/usbs_upd985xx.h,
+ src/usbs_upd985xx.c, src/usbs_upd985xx.cxx:
+ Implement workarounds for some hardware problems.
+ 1) flush the ibus after every write operation.
+ 2) implement support for endpoint 5 transfers, and
+ optionally for emulating bulk transfers over this
+ endpoint (normally this endpoint is used for interrupt
+ transfers).
+ All under the control of suitable configuration options,
+ which by default are set to work around the hardware problems.
+
+2001-08-06 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_upd985xx.c:
+ Add initial support for USB testing.
+
+2001-07-02 Bart Veer <bartv@redhat.com>
+
+ * doc/usbs_upd985xx.sgml:
+ Document alignment restrictions for receive buffers, and
+ the optional platform-specific INIT macro.
+
+ * doc/devs-usb-nec-upd985xx.html
+ Regenerate following above change.
+
+ * src/usbs_upd985xx.c:
+ Remove FIXME related to cacheline alignment, not an issue
+ for MIPS.
+ Remove FIXME related to platform-specific USB startup,
+ now implemented.
+
+2001-06-28 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_upd985xx.c, cdl/usbs_upd985xx.cdl:
+ Device driver now functional.
+
+2001-05-22 Bart Veer <bartv@redhat.com>
+
+ * USB device driver work started.
+
+//===========================================================================
+// ####GPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 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####
+//===========================================================================
diff --git a/ecos/packages/devs/usb/nec_upd985xx/current/cdl/usbs_upd985xx.cdl b/ecos/packages/devs/usb/nec_upd985xx/current/cdl/usbs_upd985xx.cdl
new file mode 100644
index 0000000..55ce56f
--- /dev/null
+++ b/ecos/packages/devs/usb/nec_upd985xx/current/cdl/usbs_upd985xx.cdl
@@ -0,0 +1,291 @@
+# ====================================================================
+#
+# usbs_upd985xx.cdl
+#
+# USB device driver for the NEC uPD985xx family of processors.
+#
+# ====================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 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): bartv
+# Original data: bartv
+# Contributors:
+# Date: 2001-05-22
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_USB_UPD985XX {
+ display "NEC uPD985xx USB Device Driver"
+ include_dir "cyg/io/usb"
+ parent CYGPKG_USB
+ implements CYGHWR_IO_USB_SLAVE
+ doc ref/devs-usb-nec-upd985xx.html
+
+ # Make sure that we are running on the right hardware.
+ requires CYGPKG_HAL_MIPS
+ requires CYGPKG_HAL_MIPS_UPD985XX
+
+ description "
+ The NEC uPD985xx family of processors implements an
+ on-chip USB device controller, facilitating the use of this
+ processor in USB peripherals. This package provides a
+ suitable eCos device driver."
+
+ cdl_component CYGFUN_DEVS_USB_UPD985XX_EP0 {
+ display "Support the control endpoint 0"
+ default_value CYGINT_IO_USB_SLAVE_CLIENTS
+ requires CYGPKG_IO_USB CYGPKG_IO_USB_SLAVE
+ compile usbs_upd985xx.c
+ compile -library=libextras.a usbs_upd985xx_data.cxx
+ description "
+ Enable support for endpoint 0. If this support is disabled
+ then the entire USB port is unusable."
+
+ cdl_option CYGVAR_DEVS_USB_UPD985XX_EP0_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 0"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If endpoint 0 will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and ioctl calls then a devtab entry is needed.
+ "
+ }
+
+ cdl_option CYGNUM_DEVS_USB_UPD985XX_EP0_PKTSIZE {
+ display "Size of endpoint 0 control packets"
+ flavor data
+ default_value 8
+ legal_values { 8 16 32 64 }
+ description "
+ Control messages on endpoint 0 are split into packets of
+ 8, 16, 32 or 64 bytes - these are the values permitted by the
+ USB specification. The same packet size is used for both
+ receives and transmits. This value must also be used for the
+ max_packet_size field of the device descriptor in the
+ application's USB enumeration data.
+
+ According to section 5.5.5 of the USB specification, if a new
+ control message is received before the previous transaction
+ has completed then the previous transaction must be aborted.
+ If that transaction involved transferring data to the host
+ then there is a problem: that data may still be queued for
+ transmission and the NEC USB device appears to provide no way
+ of aborting that transmit. The problem is unlikely to arise
+ with normal usage, but may be detected by compliance
+ testsuites. Increasing the packet size to its maximum value
+ of 64 reduces the probability of failure.
+ "
+ }
+
+ cdl_option CYGNUM_DEVS_USB_UPD985XX_EP0_TXBUFSIZE {
+ display "Size of statically-allocated endpoint 0 transmit buffer"
+ flavor data
+ default_value 256
+ requires { CYGNUM_DEVS_USB_UPD985XX_EP0_TXBUFSIZE >= CYGNUM_DEVS_USB_UPD985XX_EP0_PKTSIZE }
+ description "
+ The implementation of the support for endpoint 0 uses
+ a single static buffer to hold the response to the
+ current control message. Typically this buffer can be
+ fairly small since replies to control messages tend to
+ be small: typically some tens of bytes for the enumeration
+ data, perhaps a bit more for unicode-encoded string
+ descriptors. However if some application-specific protocol
+ depends on larger control messages then this buffer
+ size may need to be increased.
+ "
+ }
+
+ cdl_option CYGNUM_DEVS_USB_UPD985XX_EP0_RXBUFSIZE {
+ display "Size of statically-allocated endpoint 0 transmit buffer"
+ flavor data
+ default_value 64
+ requires { CYGNUM_DEVS_USB_UPD985XX_EP0_RXBUFSIZE >= CYGNUM_DEVS_USB_UPD985XX_EP0_PKTSIZE }
+ description "
+ The implementation of the support for endpoint 0 uses
+ a single static buffer to hold incoming control messages.
+ Typically this buffer can be small: standard control messages
+ involve an initial eight-byte header, sometimes followed by
+ a small amount of additional data. However if some
+ application-specific protocol depends on larger control
+ messages then this buffer size may need to be increased.
+ "
+ }
+ }
+ cdl_component CYGPKG_DEVS_USB_UPD985XX_EP3 {
+ display "Support endpoint 3, used for slave->host IN bulk transfers"
+ implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+ requires CYGFUN_DEVS_USB_UPD985XX_EP0
+ default_value 0
+ description "
+ In the uPD985xx USB implementation endpoint 3 can only be
+ used for slave->host IN bulk transfers. If the intended application
+ only involves host->slave transfers then this endpoint is
+ not relevant.
+
+ By default this endpoint is disabled: according to NEC erratum
+ U3 there may be problems when doing transfers of 192 bytes or
+ greater. Instead the interrupt endpoint 5 is used, with
+ software emulation of the bulk protocol. If the application
+ involves only transfers of less than 192 bytes then endpoint
+ 3 can be enabled.
+ "
+
+ cdl_option CYGVAR_DEVS_USB_UPD985XX_EP3_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 3"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If endpoint 3 will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and read calls then a devtab entry is needed.
+ "
+ }
+ }
+
+ cdl_component CYGPKG_DEVS_USB_UPD985XX_EP4 {
+ display "Support endpoint 4, used for slave->host OUT bulk transfers"
+ implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+ requires CYGFUN_DEVS_USB_UPD985XX_EP0
+ default_value CYGFUN_DEVS_USB_UPD985XX_EP0
+ description "
+ In the uPD985xx USB implementation endpoint 4 can only be
+ used for host->slave OUT bulk transfers. If the intended application
+ only involves slave->host transfers then the support for
+ endpoint 4 can be disabled. Note that this does not affect
+ control messages which always go via endpoint 0."
+
+ cdl_option CYGVAR_DEVS_USB_UPD985XX_EP4_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 4"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If endpoint 4 will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and write calls then a devtab entry is needed."
+ }
+ }
+
+ cdl_component CYGPKG_DEVS_USB_UPD985XX_EP5 {
+ display "Support endpoint 5, used for slave->host IN transfers"
+ implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+ requires CYGFUN_DEVS_USB_UPD985XX_EP0
+ default_value CYGFUN_DEVS_USB_UPD985XX_EP0
+ description "
+ In the uPD985xx USB implementation endpoint 5 can only be
+ used for slave->host communication. This endpoint is
+ intended primarily for interrupt transfers, but can be
+ used for bulk transfers given a small amount of additional
+ software support."
+
+ cdl_option CYGIMP_DEVS_USB_UPD985XX_EP5_BULK {
+ display "Implement bulk transfers rather than interrupt transfers"
+ default_value 1
+ description "
+ Endpoint 5 is normally used for interrupt transfers, which
+ are limited to 64 bytes. However with a little bit of software
+ support it is possible to implement bulk transfers instead.
+ With some revisions of the silicon this provides a workaround
+ for problems with endpoint 3 - NEC erratum U3 should be consulted
+ for additional information."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_UPD985XX_EP5_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 5"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If endpoint 5 will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and write calls then a devtab entry is needed.
+ "
+ }
+ }
+
+ cdl_option CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME {
+ display "Base name for devtab entries"
+ flavor data
+ active_if { CYGVAR_DEVS_USB_UPD985XX_EP0_DEVTAB_ENTRY ||
+ CYGVAR_DEVS_USB_UPD985XX_EP3_DEVTAB_ENTRY ||
+ CYGVAR_DEVS_USB_UPD985XX_EP4_DEVTAB_ENTRY
+ }
+ default_value { "\"/dev/usbs\"" }
+ description "
+ If the uPD985xx USB device driver package provides devtab
+ entries for any of the endpoints then this option gives
+ control over the names of these entries. By default the
+ endpoints will be called \"/dev/usbs0c\", \"/dev/usbs3w\"
+ and \"/dev/usbs4r\" (assuming all three endpoints are
+ enabled. The common part \"/dev/usbs\" is determined
+ by this configuration option. It may be necessary to
+ change this if there are multiple USB slave-side
+ devices on the target hardware to prevent a name clash.
+ "
+ }
+
+ cdl_option CYGIMP_DEVS_USB_UPD985XX_IBUS_WRITE_LIMIT {
+ display "Work around potential hardware problem with IBUS writes"
+ default_value 1
+ description "
+ With some revisions of the silicon there may be problems if
+ a device driver performs multiple writes to the IBUS in
+ quick succession. By default this driver avoids such problems,
+ at the cost of some cpu cycles and a small amount of extra code.
+ NEC erratum S1 should be consulted for more details."
+ }
+
+ cdl_option CYGIMP_DEVS_USB_UPD985XX_SERIALIZE_TRANSMITS {
+ display "Work around potential hardware problem with concurrent transmits"
+ default_value 1
+ description "
+ With some revisions of the silicon there may be problems if
+ the device driver is asked to perform concurrent slave->host
+ transmissions on different endpoints, for example sending
+ a reply to a control message while there is a bulk transfer
+ in progress. This option enables a workaround for the
+ problem by ensuring that only one transmit operation is in
+ progress at any one time. NEC errata U3 and U4 should be
+ consulted for more details."
+ }
+}
diff --git a/ecos/packages/devs/usb/nec_upd985xx/current/doc/devs-usb-nec-upd985xx.html b/ecos/packages/devs/usb/nec_upd985xx/current/doc/devs-usb-nec-upd985xx.html
new file mode 100644
index 0000000..6317b66
--- /dev/null
+++ b/ecos/packages/devs/usb/nec_upd985xx/current/doc/devs-usb-nec-upd985xx.html
@@ -0,0 +1,372 @@
+<!-- Copyright (C) 2001 Free Software Foundation, Inc. -->
+<!-- This material may be distributed only subject to the terms -->
+<!-- and conditions set forth in the Open Publication License, v1.0 -->
+<!-- or later (the latest version is presently available at -->
+<!-- http://www.opencontent.org/openpub/). -->
+<!-- Distribution of substantively modified versions of this -->
+<!-- document is prohibited without the explicit permission of the -->
+<!-- copyright holder. -->
+<!-- Distribution of the work or derivative of the work in any -->
+<!-- standard (paper) book form is prohibited unless prior -->
+<!-- permission is obtained from the copyright holder. -->
+<HTML
+><HEAD
+><TITLE
+>NEC uPD985xx USB Device Driver</TITLE
+><meta name="MSSmartTagsPreventParsing" content="TRUE">
+<META
+NAME="GENERATOR"
+CONTENT="Modular DocBook HTML Stylesheet Version 1.64
+"></HEAD
+><BODY
+CLASS="REFENTRY"
+BGCOLOR="#FFFFFF"
+TEXT="#000000"
+LINK="#0000FF"
+VLINK="#840084"
+ALINK="#0000FF"
+><H1
+><A
+NAME="DEVS-USB-NEC-UPD985XX"
+>NEC uPD985xx USB Device Driver</A
+></H1
+><DIV
+CLASS="REFNAMEDIV"
+><A
+NAME="AEN4"
+></A
+><H2
+>Name</H2
+>NEC uPD985xx USB Support&nbsp;--&nbsp;Device driver for the on-chip NEC uPD985xx USB device</DIV
+><DIV
+CLASS="REFSECT1"
+><A
+NAME="AEN7"
+></A
+><H2
+>NEC uPD985xx USB Hardware</H2
+><P
+>The NEC uPD985xx family of processors is supplied with an on-chip USB
+slave device, the UDC (USB Device Controller). This supports seven
+endpoints. Endpoint 0 can only be used for control messages. Endpoints
+1 and 2 are for isochronous transmits and receives respectively.
+Endpoints 3 and 4 support bulk transmits and receives. Endpoints 5 and
+6 normally support interrupt transmits and receives,but endpoint 5 can
+also be configured to support bulk transmits. At this time only the
+control endpoint 0, the bulk endpoints 3 and 4, and the interrupt
+endpoint 5 are supported.</P
+></DIV
+><DIV
+CLASS="REFSECT1"
+><A
+NAME="AEN10"
+></A
+><H2
+>Endpoint Data Structures</H2
+><P
+>The uPD985xx USB device driver can provide up to four data structures
+corresponding to the four supported endpoints: a
+<SPAN
+CLASS="STRUCTNAME"
+>usbs_control_endpoint</SPAN
+> structure
+<TT
+CLASS="VARNAME"
+>usbs_upd985xx_ep0</TT
+>;
+<SPAN
+CLASS="STRUCTNAME"
+>usbs_tx_endpoint</SPAN
+> structures
+<TT
+CLASS="VARNAME"
+>usbs_upd985xx_ep3</TT
+> and
+<TT
+CLASS="VARNAME"
+>usbs_upd985xx_ep5</TT
+>; and a
+<SPAN
+CLASS="STRUCTNAME"
+>usbs_rx_endpoint</SPAN
+>
+<TT
+CLASS="VARNAME"
+>usbs_upd985xx_ep4</TT
+>. The header file
+<TT
+CLASS="FILENAME"
+>cyg/io/usb/usbs_nec_upd985xx.h</TT
+>
+provides declarations for these.</P
+><P
+>Not all applications will require support for all the endpoints. For
+example, if the intended use of the UDC only involves peripheral to
+host transfers then <TT
+CLASS="LITERAL"
+>usbs_upd985xx_ep4</TT
+> is redundant.
+The device driver provides configuration options to control the
+presence of each endpoint:</P
+><P
+></P
+><OL
+TYPE="1"
+><LI
+><P
+>Endpoint 0 is controlled by
+<TT
+CLASS="LITERAL"
+>CYGFUN_DEVS_USB_UPD985XX_EP0</TT
+>. This defaults to
+enabled if there are any higher-level packages that require USB
+hardware or if the global preference
+<TT
+CLASS="LITERAL"
+>CYGGLO_IO_USB_SLAVE_APPLICATION</TT
+> is enabled,
+otherwise it is disabled. Usually this has the desired effect. It may
+be necessary to override this in special circumstances, for example if
+the target board uses an external USB chip in preference to the UDC
+and it is that external chip's device driver that should be used
+rather than the on-chip UDC. It is not possible to disable endpoint 0
+and at the same time enable one or both of the other endpoints, since
+a USB device is only usable if it can process the standard control
+messages.</P
+></LI
+><LI
+><P
+>Endpoint 3 is controlled by
+<TT
+CLASS="LITERAL"
+>CYGPKG_DEVS_USB_UPD985XX_EP3</TT
+>. By default this
+endpoint is disabled: according to NEC erratum U3 there may be
+problems when attempting bulk transfers of 192 bytes or greater. As an
+alternative the device driver provides endpoint 5 configured to
+support bulk transfers. Endpoint 3 can be enabled if the application
+only requires bulk transfers of less than 192 bytes, or if this
+erratum is not applicable to the system being developed for other
+reasons.</P
+></LI
+><LI
+><P
+>Similarly endpoint 4 is controlled by
+<TT
+CLASS="LITERAL"
+>CYGPKG_DEVS_USB_UPD985XX_EP4</TT
+>. This is enabled by
+default whenever endpoint 0 is enabled, but it can be disabled
+manually.</P
+></LI
+><LI
+><P
+>Endpoint 5 is controlled by
+<TT
+CLASS="LITERAL"
+>CYGPKG_DEVS_USB_UPD985XX_EP5</TT
+>. This is enabled by
+default whenever endpoint 0 is enabled, but it can be disabled
+manually. There is also a configuration option
+<TT
+CLASS="LITERAL"
+>CYGIMP_DEVS_USB_UPD985XX_EP5_BULK</TT
+>, enabled by
+default. This option allows the endpoint to be used for bulk
+transfers rather than interrupt transfers.</P
+></LI
+></OL
+><P
+>The uPD985xx USB device driver implements the interface specified by the
+common eCos USB Slave Support package. The documentation for that
+package should be consulted for further details. </P
+><P
+>The device driver assumes a bulk packet size of 64 bytes, so this
+value should be used in the endpoint descriptors in the enumeration
+data provided by application code. The device driver also assumes
+a control packet size of eight bytes, and again this should be
+reflected in the enumeration data. If endpoint 5 is configured for
+interrupt rather than bulk transfers then the maximum packet size is
+limited to 64 bytes by the USB standard.</P
+></DIV
+><DIV
+CLASS="REFSECT1"
+><A
+NAME="AEN40"
+></A
+><H2
+>Devtab Entries</H2
+><P
+>In addition to the endpoint data structures the uPD985xx USB device
+driver can also provide devtab entries for each endpoint. This allows
+higher-level code to use traditional I/O operations such as
+<TT
+CLASS="FUNCTION"
+>open</TT
+>/<TT
+CLASS="FUNCTION"
+>read</TT
+>/<TT
+CLASS="FUNCTION"
+>write</TT
+>
+rather than the USB-specific non-blocking functions like
+<TT
+CLASS="FUNCTION"
+>usbs_start_rx_buffer</TT
+>. These devtab entries are
+optional since they are not always required. The relevant
+configuration options are
+<TT
+CLASS="LITERAL"
+>CYGVAR_DEVS_USB_UPD985XX_EP0_DEVTAB_ENTRY</TT
+>,
+<TT
+CLASS="LITERAL"
+>CYGVAR_DEVS_USB_UPD985XX_EP3_DEVTAB_ENTRY</TT
+>,
+<TT
+CLASS="LITERAL"
+>CYGVAR_DEVS_USB_UPD985XX_EP4_DEVTAB_ENTRY</TT
+>, and
+<TT
+CLASS="LITERAL"
+>CYGVAR_DEVS_USB_UPD985XX_EP5_DEVTAB_ENTRY</TT
+>. By
+default these devtab entries are provided if the global preference
+<TT
+CLASS="LITERAL"
+>CYGGLO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES</TT
+> is enabled,
+which is usually the case. Obviously a devtab entry for a given
+endpoint will only be provided if the underlying endpoint is enabled.
+For example, there will not be a devtab entry for endpoint 4 if
+<TT
+CLASS="LITERAL"
+>CYGPKG_DEVS_USB_UPD985XX_EP4</TT
+> is disabled.</P
+><P
+>The names for the devtab entries are determined by using a
+configurable base name and appending <TT
+CLASS="LITERAL"
+>0c</TT
+>,
+<TT
+CLASS="LITERAL"
+>3w</TT
+>, <TT
+CLASS="LITERAL"
+>4r</TT
+> or <TT
+CLASS="LITERAL"
+>5w</TT
+>.
+The base name is determined by the configuration option
+<TT
+CLASS="LITERAL"
+>CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME</TT
+> and has a
+default value of <TT
+CLASS="LITERAL"
+>/dev/usbs</TT
+>, so the devtab entry for
+endpoint 4 would default to <TT
+CLASS="LITERAL"
+>/dev/usbs4r</TT
+>. If the
+target hardware involves multiple USB devices then application
+developers may have to change the base name to prevent a name clash
+with other USB device drivers.</P
+></DIV
+><DIV
+CLASS="REFSECT1"
+><A
+NAME="AEN61"
+></A
+><H2
+>Restrictions</H2
+><P
+>The current device driver imposes a restriction on certain bulk
+receives on endpoint 4. If the protocol being used involves
+variable-length transfers, in other words if the host is allowed to
+send less data than a maximum-sized transfer, then the buffer passed
+to the device driver for receives must be aligned to a 16-byte
+cacheline boundary and it must be a multiple of this 16-byte cacheline
+size. This restriction does not apply if the protocol only involves
+fixed-size transfers.</P
+></DIV
+><DIV
+CLASS="REFSECT1"
+><A
+NAME="AEN64"
+></A
+><H2
+>Optional Hardware Workarounds</H2
+><P
+>The NEC errata list a number of other problems that affect the
+USB device driver. The device driver contains workarounds for these,
+which are enabled by default but can be disabled if the application
+developer knows that the relevant errata are not relevant to the
+system being developed.</P
+><P
+>Erratum S1 lists a possible problem if the device driver attempts
+multiple writes to the USB hardware. This is circumvented by a
+dummy read operation after every write. If this workaround is not
+required then the configuration option
+<TT
+CLASS="LITERAL"
+>CYGIMP_DEVS_USB_UPD985XX_IBUS_WRITE_LIMIT</TT
+> can be disabled.</P
+><P
+>Errata U3 and U4 describe various problems related to concurrent
+transmissions on different endpoints. By default the device driver
+works around this by serializing all transmit operations. For example
+if the device driver needs to send a response to a control message on
+endpoint 0 while there is an ongoing bulk transfer on endpoint 5, the
+response is delayed until the bulk transfer has completed. Under
+typical operating conditions this does not cause any problems:
+endpoint 0 traffic usually happens only during initialization, when
+the target is connected to the host, while endpoint 5 traffic only
+happens after initialization. However if transmit serialization is
+inappropriate for the system being developed then it can be disabled
+using the configuration option
+<TT
+CLASS="LITERAL"
+>CYGIMP_DEVS_USB_UPD985XX_SERIALIZE_TRANSMITS</TT
+>. </P
+></DIV
+><DIV
+CLASS="REFSECT1"
+><A
+NAME="AEN71"
+></A
+><H2
+>Platform Dependencies</H2
+><P
+>On some platforms it is necessary for the low-level USB device driver
+to perform some additional operations during start-up. For example it
+may be necessary to manipulate one of the processor's GPIO lines
+before the host can detect a new USB peripheral and attempt to
+communicate with it. This avoids problems if the target involves a
+significant amount of work prior to device driver initialization, for
+example a power-on self-test sequence. If the USB host attempted to
+contact the target before the USB device driver had been initialized,
+it would fail to get the expected responses and conclude that the
+target was not a functional USB peripheral.</P
+><P
+>Platform-specific initialization code can be provided via a macro
+<TT
+CLASS="FUNCTION"
+>UPD985XX_USB_PLATFORM_INIT</TT
+>. Typically this macro
+would be defined in the platform HAL's header file
+<TT
+CLASS="FILENAME"
+>cyg/hal/plf_io.h</TT
+>. If the
+current platform defines such a macro, the USB device driver will
+invoke it during the endpoint 0 start-up operation.</P
+></DIV
+></BODY
+></HTML
+>
diff --git a/ecos/packages/devs/usb/nec_upd985xx/current/doc/makefile b/ecos/packages/devs/usb/nec_upd985xx/current/doc/makefile
new file mode 100644
index 0000000..ed36ede
--- /dev/null
+++ b/ecos/packages/devs/usb/nec_upd985xx/current/doc/makefile
@@ -0,0 +1,54 @@
+#=============================================================================
+#
+# makefile
+#
+# For building the NEC uPD985xx USB device driver documentation
+#
+#=============================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 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): bartv
+# Date: 2001-01-11
+#####DESCRIPTIONEND####
+#=============================================================================
+
+TOPLEVEL := ../../../../..
+MAIN_SGML := usbs_upd985xx.sgml
+MAIN_HTML := devs-usb-upd985xx.html
+MAIN_PDF := devs-usb-upd985xx.pdf
+OTHER_SGML :=
+PICTURES :=
+
+include $(TOPLEVEL)/pkgconf/rules.doc
diff --git a/ecos/packages/devs/usb/nec_upd985xx/current/doc/usbs_upd985xx.sgml b/ecos/packages/devs/usb/nec_upd985xx/current/doc/usbs_upd985xx.sgml
new file mode 100644
index 0000000..74a4e4c
--- /dev/null
+++ b/ecos/packages/devs/usb/nec_upd985xx/current/doc/usbs_upd985xx.sgml
@@ -0,0 +1,257 @@
+<!-- DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" -->
+
+<!-- {{{ Banner -->
+
+<!-- =============================================================== -->
+<!-- -->
+<!-- usbs_nec_upd9850x.sgml -->
+<!-- -->
+<!-- Documentation for the NEC uPD9850x USB Device Driver. -->
+<!-- -->
+<!-- =============================================================== -->
+<!-- ####ECOSDOCCOPYRIGHTBEGIN#### -->
+<!-- =============================================================== -->
+<!-- Copyright (C) 2001, 2002 Free Software Foundation, Inc. -->
+<!-- This material may be distributed only subject to the terms -->
+<!-- and conditions set forth in the Open Publication License, v1.0 -->
+<!-- or later (the latest version is presently available at -->
+<!-- http://www.opencontent.org/openpub/) -->
+<!-- Distribution of the work or derivative of the work in any -->
+<!-- standard (paper) book form is prohibited unless prior -->
+<!-- permission obtained from the copyright holder -->
+<!-- =============================================================== -->
+<!-- ####ECOSDOCCOPYRIGHTEND#### -->
+<!-- =============================================================== -->
+<!-- #####DESCRIPTIONBEGIN#### -->
+<!-- -->
+<!-- Author(s): bartv -->
+<!-- Contact(s): bartv -->
+<!-- Date: 2001/05/22 -->
+<!-- Version: 0.01 -->
+<!-- -->
+<!-- ####DESCRIPTIONEND#### -->
+<!-- =============================================================== -->
+
+<!-- }}} -->
+
+<part id="devs-usb-nec-upd985xx-ref">
+<!-- reference id="devs-usb-nec-upd985xx-ref" -->
+ <title>NEC uPD985xx USB Device Driver</title>
+
+<refentry id="devs-usb-nec-upd985xx">
+<refmeta>
+<refentrytitle>NEC uPD985xx USB Device Driver</refentrytitle>
+</refmeta>
+<refnamediv>
+<refname>NEC uPD985xx USB Support</refname>
+<refpurpose>Device driver for the on-chip NEC uPD985xx USB device</refpurpose>
+</refnamediv>
+
+<refsect1><title>NEC uPD985xx USB Hardware</title>
+<para>
+The NEC uPD985xx family of processors is supplied with an on-chip USB
+slave device, the UDC (USB Device Controller). This supports seven
+endpoints. Endpoint 0 can only be used for control messages. Endpoints
+1 and 2 are for isochronous transmits and receives respectively.
+Endpoints 3 and 4 support bulk transmits and receives. Endpoints 5 and
+6 normally support interrupt transmits and receives, but endpoint 5 can
+also be configured to support bulk transmits. At this time only the
+control endpoint 0, the bulk endpoints 3 and 4, and the interrupt
+endpoint 5 are supported.
+</para>
+</refsect1>
+
+<refsect1><title>Endpoint Data Structures</title>
+<para>
+The uPD985xx USB device driver can provide up to four data structures
+corresponding to the four supported endpoints: a
+<structname>usbs_control_endpoint</structname> structure
+<varname>usbs_upd985xx_ep0</varname>;
+<structname>usbs_tx_endpoint</structname> structures
+<varname>usbs_upd985xx_ep3</varname> and
+<varname>usbs_upd985xx_ep5</varname>; and a
+<structname>usbs_rx_endpoint</structname>
+<varname>usbs_upd985xx_ep4</varname>. The header file
+<filename class="headerfile">cyg/io/usb/usbs_nec_upd985xx.h</filename>
+provides declarations for these.
+</para>
+<para>
+Not all applications will require support for all the endpoints. For
+example, if the intended use of the UDC only involves peripheral to
+host transfers then <literal>usbs_upd985xx_ep4</literal> is redundant.
+The device driver provides configuration options to control the
+presence of each endpoint:
+</para>
+<orderedlist>
+<listitem>
+<para>
+Endpoint 0 is controlled by
+<literal>CYGFUN_DEVS_USB_UPD985XX_EP0</literal>. This defaults to
+enabled if there are any higher-level packages that require USB
+hardware or if the global preference
+<literal>CYGGLO_IO_USB_SLAVE_APPLICATION</literal> is enabled,
+otherwise it is disabled. Usually this has the desired effect. It may
+be necessary to override this in special circumstances, for example if
+the target board uses an external USB chip in preference to the UDC
+and it is that external chip's device driver that should be used
+rather than the on-chip UDC. It is not possible to disable endpoint 0
+and at the same time enable one or both of the other endpoints, since
+a USB device is only usable if it can process the standard control
+messages.
+</para>
+</listitem>
+<listitem>
+<para>
+Endpoint 3 is controlled by
+<literal>CYGPKG_DEVS_USB_UPD985XX_EP3</literal>. By default this
+endpoint is disabled: according to NEC erratum U3 there may be
+problems when attempting bulk transfers of 192 bytes or greater. As an
+alternative the device driver provides support for endpoint 5,
+configured to allow bulk transfers. Endpoint 3 can be enabled if the
+application only requires bulk transfers of less than 192 bytes, or if
+this erratum is not applicable to the system being developed for other
+reasons.
+</para>
+</listitem>
+<listitem>
+<para>
+Endpoint 4 is controlled by
+<literal>CYGPKG_DEVS_USB_UPD985XX_EP4</literal>. This is enabled by
+default whenever endpoint 0 is enabled, but it can be disabled
+manually.
+</para>
+</listitem>
+<listitem>
+<para>
+Endpoint 5 is controlled by
+<literal>CYGPKG_DEVS_USB_UPD985XX_EP5</literal>. This is enabled by
+default whenever endpoint 0 is enabled, but it can be disabled
+manually. There is also a configuration option
+<literal>CYGIMP_DEVS_USB_UPD985XX_EP5_BULK</literal>, enabled by
+default. This option allows the endpoint to be used for bulk
+transfers rather than interrupt transfers.
+</para>
+</listitem>
+</orderedlist>
+<para>
+The uPD985xx USB device driver implements the interface specified by the
+common eCos USB Slave Support package. The documentation for that
+package should be consulted for further details.
+</para>
+<para>
+The device driver assumes a bulk packet size of 64 bytes, so this
+value should be used in the endpoint descriptors in the enumeration
+data provided by application code. The device driver also assumes
+a control packet size of eight bytes, and again this should be
+reflected in the enumeration data. If endpoint 5 is configured for
+interrupt rather than bulk transfers then the maximum packet size is
+limited to 64 bytes by the USB standard.
+</para>
+</refsect1>
+
+<refsect1><title>Devtab Entries</title>
+<para>
+In addition to the endpoint data structures the uPD985xx USB device
+driver can also provide devtab entries for each endpoint. This allows
+higher-level code to use traditional I/O operations such as
+<function>open</function>/<function>read</function>/<function>write</function>
+rather than the USB-specific non-blocking functions like
+<function>usbs_start_rx_buffer</function>. These devtab entries are
+optional since they are not always required. The relevant
+configuration options are
+<literal>CYGVAR_DEVS_USB_UPD985XX_EP0_DEVTAB_ENTRY</literal>,
+<literal>CYGVAR_DEVS_USB_UPD985XX_EP3_DEVTAB_ENTRY</literal>,
+<literal>CYGVAR_DEVS_USB_UPD985XX_EP4_DEVTAB_ENTRY</literal>, and
+<literal>CYGVAR_DEVS_USB_UPD985XX_EP5_DEVTAB_ENTRY</literal>. By
+default these devtab entries are provided if the global preference
+<literal>CYGGLO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES</literal> is enabled,
+which is usually the case. Obviously a devtab entry for a given
+endpoint will only be provided if the underlying endpoint is enabled.
+For example, there will not be a devtab entry for endpoint 4 if
+<literal>CYGPKG_DEVS_USB_UPD985XX_EP4</literal> is disabled.
+</para>
+<para>
+The names for the devtab entries are determined by using a
+configurable base name and appending <literal>0c</literal>,
+<literal>3w</literal>, <literal>4r</literal> or <literal>5w</literal>.
+The base name is determined by the configuration option
+<literal>CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME</literal> and has a
+default value of <literal>/dev/usbs</literal>, so the devtab entry for
+endpoint 4 would default to <literal>/dev/usbs4r</literal>. If the
+target hardware involves multiple USB devices then application
+developers may have to change the base name to prevent a name clash
+with other USB device drivers.
+</para>
+</refsect1>
+
+<refsect1><title>Restrictions</title>
+<para>
+The current device driver imposes a restriction on certain bulk
+receives on endpoint 4. If the protocol being used involves
+variable-length transfers, in other words if the host is allowed to
+send less data than a maximum-sized transfer, then the buffer passed
+to the device driver for receives must be aligned to a 16-byte
+cacheline boundary and it must be a multiple of this 16-byte cacheline
+size. This restriction does not apply if the protocol only involves
+fixed-size transfers.
+</para>
+</refsect1>
+
+<refsect1><title>Optional Hardware Workarounds</title>
+<para>
+The NEC errata list a number of other problems that affect the USB
+device driver. The device driver contains workarounds for these, which
+are enabled by default but can be disabled if the application
+developer knows that the errata are not relevant to the system being
+developed.
+</para>
+<para>
+Erratum S1 lists a possible problem if the device driver attempts
+multiple writes to the USB hardware. This is circumvented by a
+dummy read operation after every write. If the workaround is not
+required then the configuration option
+<literal>CYGIMP_DEVS_USB_UPD985XX_IBUS_WRITE_LIMIT</literal> can be disabled.
+</para>
+<para>
+Errata U3 and U4 describe various problems related to concurrent
+transmissions on different endpoints. By default the device driver
+works around this by serializing all transmit operations. For example
+if the device driver needs to send a response to a control message on
+endpoint 0 while there is an ongoing bulk transfer on endpoint 5, the
+response is delayed until the bulk transfer has completed. Under
+typical operating conditions this does not cause any problems:
+endpoint 0 traffic usually happens only during initialization, when
+the target is connected to the host, while endpoint 5 traffic only
+happens after initialization. However if transmit serialization is
+inappropriate for the system being developed then it can be disabled
+using the configuration option
+<literal>CYGIMP_DEVS_USB_UPD985XX_SERIALIZE_TRANSMITS</literal>.
+</para>
+</refsect1>
+
+<refsect1><title>Platform Dependencies</title>
+<para>
+On some platforms it is necessary for the low-level USB device driver
+to perform some additional operations during start-up. For example it
+may be necessary to manipulate one of the processor's GPIO lines
+before the host can detect a new USB peripheral and attempt to
+communicate with it. This avoids problems if the target involves a
+significant amount of work prior to device driver initialization, for
+example a power-on self-test sequence. If the USB host attempted to
+contact the target before the USB device driver had been initialized,
+it would fail to get the expected responses and conclude that the
+target was not a functional USB peripheral.
+</para>
+<para>
+Platform-specific initialization code can be provided via a macro
+<function>UPD985XX_USB_PLATFORM_INIT</function>. Typically this macro
+would be defined in the platform HAL's header file
+<filename class="headerfile">cyg/hal/plf_io.h</filename>. If the
+current platform defines such a macro, the USB device driver will
+invoke it during the endpoint 0 start-up operation.
+</para>
+</refsect1>
+
+</refentry>
+</part>
+<!-- /reference -->
diff --git a/ecos/packages/devs/usb/nec_upd985xx/current/include/usbs_upd985xx.h b/ecos/packages/devs/usb/nec_upd985xx/current/include/usbs_upd985xx.h
new file mode 100644
index 0000000..96b1b9d
--- /dev/null
+++ b/ecos/packages/devs/usb/nec_upd985xx/current/include/usbs_upd985xx.h
@@ -0,0 +1,78 @@
+#ifndef CYGONCE_USBS_UPD985XX_H
+# define CYGONCE_USBS_UPD985XX_H
+//==========================================================================
+//
+// include/usbs_upd985xx.h
+//
+// The interface exported by the NEC uPD985xx USB device driver
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 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): bartv
+// Contributors: bartv
+// Date: 2000-05-22
+// Purpose:
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/io/usb/usbs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The NEC UPD985xx family comes with on-chip USB slave support. This
+ * provides seven endpoints. Endpoint 0 can only be used for control
+ * messages. Endpoints 1 and 2 can only be used for isochronous
+ * transfers, and are not supported at this time. Endpoints 3 and 4
+ * are for bulk transfers, although endpoint 3 is normally disabled
+ * and endpoint 5 is used for bulk transfers instead. Endpoints 5
+ * and 6 are normally used for interrupt transfers, but endpoint 5 can
+ * also be used bulk transfers. Endpoint 6 is not currently supported.
+ */
+extern usbs_control_endpoint usbs_upd985xx_ep0;
+extern usbs_tx_endpoint usbs_upd985xx_ep3;
+extern usbs_rx_endpoint usbs_upd985xx_ep4;
+extern usbs_tx_endpoint usbs_upd985xx_ep5;
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+
+#endif /* CYGONCE_USBS_UPD985XX_H */
diff --git a/ecos/packages/devs/usb/nec_upd985xx/current/src/usbs_upd985xx.c b/ecos/packages/devs/usb/nec_upd985xx/current/src/usbs_upd985xx.c
new file mode 100644
index 0000000..b104edf
--- /dev/null
+++ b/ecos/packages/devs/usb/nec_upd985xx/current/src/usbs_upd985xx.c
@@ -0,0 +1,2687 @@
+//==========================================================================
+//
+// usbs_upd985xx.c
+//
+// Driver for the NEC uPD985xx USB device
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 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): bartv
+// Contributors: bartv
+// Date: 2001-05-22
+//
+// This code implements support for the on-chip USB port on the NEC
+// uPD985xx family of processors. The code has been developed on the
+// uPD98503 and may or may not work on other members of the uPD985xx
+// family.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+#include <cyg/infra/diag.h>
+
+#include <pkgconf/hal_mips_upd985xx.h>
+#include <pkgconf/devs_usb_upd985xx.h>
+
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_cache.h>
+#include <cyg/error/codes.h>
+
+#include <cyg/io/usb/usb.h>
+#include <cyg/io/usb/usbs.h>
+
+// For memcpy()
+#include <string.h>
+
+// ----------------------------------------------------------------------------
+// Toplevel FIXME's.
+//
+// The device supports remote wakeups, but this driver does not. Note that
+// the device GET_STATUS, SET_FEATURE and CLEAR_FEATURE operations are
+// affected by remote wakeup support.
+
+// ----------------------------------------------------------------------------
+// Debugging-related odds and ends.
+#if 0
+# define DBG(a) diag_printf a
+#else
+# define DBG(a)
+#endif
+
+// ----------------------------------------------------------------------------
+// Hardware definitions.
+//
+// The NEC uPD985Xx on-chip USB device provides the following:
+//
+// endpoint 0 - control messages only
+// endpoint 1 - isochronous transmits
+// endpoint 2 - isochronous receives
+// endpoint 3 - bulk transmits
+// endpoint 4 - bulk receives
+// endpoint 5 - interrupt transmits
+// endpoint 6 - interrupt receives
+
+// All acess to the USB controller registers goes via the IBUS, which
+// always runs little-endian. Hence when the CPU is running
+// little-endian no extra work is needed, but when running big-endian
+// all register updates involve swapping.
+#ifdef CYGPKG_HAL_MIPS_LSBFIRST
+# define IBUS_SWAP32(_a_) (_a_)
+# define IBUS_SWAPPTR(_type_, _a_) (_a_)
+#else
+# error IBUS_SWAP32() needs to be defined and tested
+#endif
+
+// Move an address to kseg1. Or'ing in the relevant bits means
+// that this macro will work even if the specified address is
+// already in kseg1
+#define MIPS_TO_UNCACHED(_a_) ((void*)(((cyg_uint32)(_a_)) | MIPS_KSEG1_BASE))
+
+// For now access the various registers directly. A structure might
+// be marginally more inefficient in that if a function accesses
+// several registers this could be handled by a single base plus
+// offsets, rather than by separate addresses.
+#define USBS_REGISTER(_a_) ((volatile cyg_uint32*)(MIPS_IO_BASE + UPD985XX_SYSUSB_OFF + (_a_)))
+#define USBS_ADDRREG(_type_, _a_) ((_type_* volatile*)(MIPS_IO_BASE + UPD985XX_SYSUSB_OFF + (_a_)))
+
+#define USBS_GMR USBS_REGISTER(0x0000)
+#define USBS_VER USBS_REGISTER(0x0004)
+#define USBS_GSR1 USBS_REGISTER(0x0010)
+#define USBS_IMR1 USBS_REGISTER(0x0014)
+#define USBS_GSR2 USBS_REGISTER(0x0018)
+#define USBS_IMR2 USBS_REGISTER(0x001C)
+#define EP0_CR USBS_REGISTER(0x0020)
+#define EP1_CR USBS_REGISTER(0x0024)
+#define EP2_CR USBS_REGISTER(0x0028)
+#define EP3_CR USBS_REGISTER(0x002C)
+#define EP4_CR USBS_REGISTER(0x0030)
+#define EP5_CR USBS_REGISTER(0x0034)
+#define EP6_CR USBS_REGISTER(0x0038)
+#define USBS_CMR USBS_REGISTER(0x0040)
+#define USBS_CA USBS_ADDRREG(void, 0x0044)
+#define USBS_TEPSR USBS_REGISTER(0x0048)
+#define USBS_RP0IR USBS_REGISTER(0x0050)
+#define USBS_RP0AR USBS_ADDRREG(RxBufferDescriptor, 0x0054)
+#define USBS_RP1IR USBS_REGISTER(0x0058)
+#define USBS_RP1AR USBS_ADDRREG(RxBufferDescriptor, 0x005C)
+#define USBS_RP2IR USBS_REGISTER(0x0060)
+#define USBS_RP2AR USBS_ADDRREG(RxBufferDescriptor, 0x0064)
+#define USBS_TMSA USBS_ADDRREG(TxMailbox, 0x0070)
+#define USBS_TMBA USBS_ADDRREG(TxMailbox, 0x0074)
+#define USBS_TMRA USBS_ADDRREG(TxMailbox, 0x0078)
+#define USBS_TMWA USBS_ADDRREG(TxMailbox, 0x007C)
+#define USBS_RMSA USBS_ADDRREG(RxMailbox, 0x0080)
+#define USBS_RMBA USBS_ADDRREG(RxMailbox, 0x0084)
+#define USBS_RMRA USBS_ADDRREG(RxMailbox, 0x0088)
+#define USBS_RMWA USBS_ADDRREG(RxMailbox, 0x008C)
+
+// There are additional counter registers from offset 0x100 onwards.
+// These registers are not used by the driver, and anyway may not be
+// available on all hardware.
+
+// The General Mode register USBS_GMR
+#define USBS_GMR_VT (0x01 << 23)
+#define USBS_GMR_FA_MASK (0x7F << 16)
+#define USBS_GMR_FA_SHIFT 16
+#define USBS_GMR_SOFINTVL_MASK (0x00FF << 8)
+#define USBS_GMR_SOFINTVL_SHIFT 8
+#define USBS_GMR_SOFINTVL_DEFAULT_VALUE (0x18 << 8)
+#define USBS_GMR_AU (0x01 << 2)
+#define USBS_GMR_LE (0x01 << 1)
+#define USBS_GMR_RR (0x01 << 0)
+
+// The Frame Number/Version register
+#define USBS_VER_UVER_MASK (0x0FFFF << 16)
+#define USBS_VER_UVER_SHIFT 16
+#define USBS_VER_UFNR_MASK (0x03FF << 0)
+#define USBS_VER_UFNR_SHIFT 0
+
+// General status register 1
+#define USBS_GSR1_GSR2 (0x01 << 31)
+#define USBS_GSR1_TMF (0x01 << 23)
+#define USBS_GSR1_RMF (0x01 << 22)
+#define USBS_GSR1_RPE2 (0x01 << 21)
+#define USBS_GSR1_RPE1 (0x01 << 20)
+#define USBS_GSR1_RPE0 (0x01 << 19)
+#define USBS_GSR1_RPA2 (0x01 << 18)
+#define USBS_GSR1_RPA1 (0x01 << 17)
+#define USBS_GSR1_RPA0 (0x01 << 16)
+#define USBS_GSR1_DER (0x01 << 10)
+#define USBS_GSR1_EP2FO (0x01 << 9)
+#define USBS_GSR1_EP1FU (0x01 << 8)
+#define USBS_GSR1_EP6RF (0x01 << 7)
+#define USBS_GSR1_EP5TF (0x01 << 6)
+#define USBS_GSR1_EP4RF (0x01 << 5)
+#define USBS_GSR1_EP3TF (0x01 << 4)
+#define USBS_GSR1_EP2RF (0x01 << 3)
+#define USBS_GSR1_EP1TF (0x01 << 2)
+#define USBS_GSR1_EP0RF (0x01 << 1)
+#define USBS_GSR1_EP0TF (0x01 << 0)
+
+// The Interrupt mask 1 bits correspond to the GSR1 bits above
+
+// General status register 2
+#define USBS_GSR2_FW (0x01 << 21)
+#define USBS_GSR2_IFN (0x01 << 20)
+#define USBS_GSR2_IEA (0x01 << 19)
+#define USBS_GSR2_URSM (0x01 << 18)
+#define USBS_GSR2_URST (0x01 << 17)
+#define USBS_GSR2_USPD (0x01 << 16)
+#define USBS_GSR2_EP2OS (0x01 << 7)
+#define USBS_GSR2_EP2ED (0x01 << 6)
+#define USBS_GSR2_EP2ND (0x01 << 5)
+#define USBS_GSR2_EP1NT (0x01 << 4)
+#define USBS_GSR2_EP1ET (0x01 << 3)
+#define USBS_GSR2_EP1ND (0x01 << 2)
+#define USBS_GSR2_ES (0x01 << 1)
+#define USBS_GSR2_SL (0x01 << 0)
+
+// Interrupt mask 2 bits correspond to GSR2
+
+// Endpoint control registers.
+// EP0 - control messages
+#define EP0_CR_EP0EN (0x01 << 31)
+#define EP0_CR_ISS (0x01 << 20)
+#define EP0_CR_INAK (0x01 << 19)
+#define EP0_CR_OSS (0x01 << 18)
+#define EP0_CR_NHSK0 (0x01 << 17)
+#define EP0_CR_ONAK (0x01 << 16)
+#define EP0_CR_MAXP0_MASK (0x7F << 0)
+#define EP0_CR_MAXP0_SHIFT 0
+
+// EP1 - isochronous transmits
+#define EP1_CR_EP1EN (0x01 << 31)
+#define EP1_CR_TM1 (0x01 << 19)
+#define EP1_CR_TM1_MASK (0x01 << 19)
+#define EP1_CR_TM1_SZLP (0x00 << 19)
+#define EP1_CR_TM1_NZLP (0x01 << 19)
+#define EP1_CR_MAXP1_MASK (0x3FF << 0)
+#define EP1_CR_MAXP1_SHIFT 0
+
+// EP2 - isochronous receives
+#define EP2_CR_EP2EN (0x01 << 31)
+#define EP2_CR_RM2_MASK (0x03 << 19)
+#define EP2_CR_RM2_NORMAL (0x00 << 19)
+#define EP2_CR_RM2_ASSEMBLE (0x02 << 19)
+#define EP2_CR_RM2_SEPARATE (0x03 << 19)
+#define EP2_CR_MAXP2_MASK (0x3FF << 0)
+#define EP2_CR_MAXP2_SHIFT 0
+
+// EP3 - bulk transmits
+#define EP3_CR_EP3EN (0x01 << 31)
+#define EP3_CR_TM3 (0x01 << 19)
+#define EP3_CR_TM3_MASK (0x01 << 19)
+#define EP3_CR_TM3_SZLP (0x00 << 19)
+#define EP3_CR_TM3_NZLP (0x01 << 19)
+#define EP3_CR_SS3 (0x01 << 18)
+#define EP3_CR_NAK3 (0x01 << 16)
+#define EP3_CR_MAXP3_MASK (0x7F << 0)
+#define EP3_CR_MAXP3_SHIFT 0
+
+// EP4 - bulk receives
+#define EP4_CR_EP4EN (0x01 << 31)
+#define EP4_CR_RM4_MASK (0x03 << 19)
+#define EP4_CR_RM4_NORMAL (0x00 << 19)
+#define EP4_CR_RM4_ASSEMBLE (0x02 << 19)
+#define EP4_CR_RM4_SEPARATE (0x03 << 19)
+#define EP4_CR_SS4 (0x01 << 18)
+#define EP4_CR_NHSK4 (0x01 << 17)
+#define EP4_CR_NAK4 (0x01 << 16)
+#define EP4_CR_MAXP4_MASK (0x7F << 0)
+#define EP4_CR_MAXP4_SHIFT 0
+
+// EP5 - interrupt transmits
+#define EP5_CR_EP5EN (0x01 << 31)
+#define EP5_CR_FM (0x01 << 19)
+#define EP5_CR_SS5 (0x01 << 18)
+#define EP5_CR_NAK5 (0x01 << 16)
+#define EP5_CR_MAXP5_MASK (0x7F << 0)
+#define EP5_CR_MAXP5_SHIFT 0
+
+// EP6 - interrupt receives
+#define EP6_CR_EP6EN (0x01 << 31)
+#define EP6_CR_SS6 (0x01 << 18)
+#define EP6_CR_NHSK6 (0x01 << 17)
+#define EP6_CR_NAK6 (0x01 << 16)
+#define EP6_CR_MAXP6_MASK (0x7F << 0)
+#define EP6_CR_MAXP6_SHIFT 0
+
+// Some bits which can be applied to multiple transmit or receive
+// endpoint control registers, thus avoiding unnecessary code
+// duplication. These will not work for the isochronous endpoints
+// because those are just too special.
+#define EPtx_CR_EpxEN (0x01 << 31)
+#define EPtx_CR_SSx (0x01 << 18)
+#define EPtx_CR_NAKx (0x01 << 16)
+#define EPtx_CR_MAXPx_MASK (0x7F << 0)
+#define EPtx_CR_MAXPx_SHIFT 0
+
+#define EPrx_CR_EPxEN (0x01 << 31)
+#define EPrx_CR_SSx (0x01 << 18)
+#define EPrx_CR_NHSKx (0x01 << 17)
+#define EPrx_CR_NAKx (0x01 << 16)
+#define EPrx_CR_MAXPx_MASK (0x7F << 0)
+#define EPrx_CR_MAXPx_SHIFT 0
+
+// USB command register
+#define USBS_CMR_BUSY (0x01 << 31)
+#define USBS_CMR_COMMAND_MASK (0x07 << 24)
+#define USBS_CMR_COMMAND_SHIFT 24
+#define USBS_CMR_COMMAND_TX_EP0 (0x00 << 24)
+#define USBS_CMR_COMMAND_TX_EP1 (0x01 << 24)
+#define USBS_CMR_COMMAND_TX_EP3 (0x02 << 24)
+#define USBS_CMR_COMMAND_TX_EP5 (0x03 << 24)
+#define USBS_CMR_COMMAND_ADD_POOL0 (0x04 << 24)
+#define USBS_CMR_COMMAND_ADD_POOL1 (0x05 << 24)
+#define USBS_CMR_COMMAND_ADD_POOL2 (0x06 << 24)
+#define USBS_CMR_SIZE_MASK (0x0FFFF << 0)
+#define USBS_CMR_SIZE_SHIFT 0
+
+// TX Endpoint status
+#define USBS_TEPSR_EP5TS_MASK (0x03 << 24)
+#define USBS_TEPSR_EP5TS_SHIFT 24
+#define USBS_TEPSR_EP5TS_IDLE (0x00 << 24)
+#define USBS_TEPSR_EP5TS_ONE (0x01 << 24)
+#define USBS_TEPSR_EP5TS_TWO (0x02 << 24)
+#define USBS_TEPSR_EP3TS_MASK (0x03 << 16)
+#define USBS_TEPSR_EP3TS_SHIFT 16
+#define USBS_TEPSR_EP3TS_IDLE (0x00 << 16)
+#define USBS_TEPSR_EP3TS_ONE (0x01 << 16)
+#define USBS_TEPSR_EP3TS_TWO (0x02 << 16)
+#define USBS_TEPSR_EP1TS_MASK (0x03 << 8)
+#define USBS_TEPSR_EP1TS_SHIFT 8
+#define USBS_TEPSR_EP1TS_IDLE (0x00 << 8)
+#define USBS_TEPSR_EP1TS_ONE (0x01 << 8)
+#define USBS_TEPSR_EP1TS_TWO (0x02 << 8)
+#define USBS_TEPSR_EP0TS_MASK (0x03 << 0)
+#define USBS_TEPSR_EP0TS_SHIFT 0
+#define USBS_TEPSR_EP0TS_IDLE (0x00 << 0)
+#define USBS_TEPSR_EP0TS_ONE (0x01 << 0)
+#define USBS_TEPSR_EP0TS_TWO (0x02 << 0)
+
+// Receive pools. The RP0IR, RP1IR and RP2IR registers
+// all use the same bits.
+#define USBS_RPxIR_AL_MASK (0x07 << 28)
+#define USBS_RPxIR_AL_SHIFT 28
+#define USBS_RPxIR_AL_NONE (0 << 28)
+#define USBS_RPxIR_RNOD_MASK (0x0FFFF << 0)
+#define USBS_RPxIR_RNOD_SHIFT 0
+
+// The other registers do not have special bits.
+
+// Data transfers involve buffer descriptors and mailboxes. The
+// relevant data structures and fields need to be defined. For now
+// assume 32-bit mode of operation, i.e. there will be no padding
+// between two successive 32-bit entities
+
+// A transmit packet directory consists of up to 255 buffer
+// descriptors. Each buffer descriptor specifies a buffer and a size
+// of up to 64K.
+
+typedef struct TxBufferDescriptor {
+ cyg_uint32 control;
+ void* buffer;
+} TxBufferDescriptor;
+
+#define TXBUFDESC_CTRL_LAST (0x01 << 31)
+#define TXBUFDESC_CTRL_BUFDESC_MASK (0x01 << 30)
+#define TXBUFDESC_CTRL_BUFDESC_SHIFT 30
+#define TXBUFDESC_CTRL_BUFDESC_LINK (0x00 << 30)
+#define TXBUFDESC_CTRL_BUFDESC_BUFDESC (0x01 << 30)
+#define TXBUFDESC_CTRL_SIZE_MASK (0x0FFFF << 0)
+#define TXBUFDESC_CTRL_SIZE_SHIFT 0
+
+// The result of a transmit operation gets written to a mailbox
+// structure in memory.
+typedef struct TxMailbox {
+ cyg_uint32 status;
+} TxMailbox;
+
+#define TXMBOX_STATUS_IBUS_ERROR (0x01 << 10)
+#define TXMBOX_STATUS_UNDERRUN (0x01 << 9)
+#define TXMBOX_STATUS_MODE_MASK (0x01 << 8)
+#define TXMBOX_STATUS_MODE_SHIFT 8
+#define TXMBOX_STATUS_MODE_SZLP (0x00 << 8)
+#define TXMBOX_STATUS_MODE_NZLP (0x01 << 8)
+#define TXMBOX_STATUS_EPN_MASK (0x07 << 0)
+#define TXMBOX_STATUS_EPN_SHIFT 0
+#define TXMBOX_STATUS_EPN_EP0 (0x00 << 0)
+#define TXMBOX_STATUS_EPN_EP1 (0x02 << 0)
+#define TXMBOX_STATUS_EPN_EP3 (0x04 << 0)
+#define TXMBOX_STATUS_EPN_EP5 (0x06 << 0)
+
+// Now for receive operations. This involves adding buffer descriptors
+// to one of three pools. The pools are managed by registers.
+typedef struct RxBufferDescriptor {
+ cyg_uint32 control;
+ void* buffer;
+} RxBufferDescriptor;
+
+#define RXBUFDESC_CTRL_LAST (0x01 << 31)
+#define RXBUFDESC_CTRL_BUFDESC_MASK (0x01 << 30)
+#define RXBUFDESC_CTRL_BUFDESC_SHIFT 30
+#define RXBUFDESC_CTRL_BUFDESC_LINK (0x00 << 30)
+#define RXBUFDESC_CTRL_BUFDESC_BUFDESC (0x01 << 30)
+#define RXBUFDESC_CTRL_SIZE_MASK (0x0FFFF << 0)
+#define RXBUFDESC_CTRL_SIZE_SHIFT 0
+
+typedef struct RxMailbox {
+ cyg_uint32 status;
+ void* address;
+} RxMailbox;
+
+#define RXMBOX_STATUS_EPN_MASK (0x07 << 29)
+#define RXMBOX_STATUS_EPN_SHIFT 29
+#define RXMBOX_STATUS_EPN_EP0 (0x01 << 29)
+#define RXMBOX_STATUS_EPN_EP2 (0x03 << 29)
+#define RXMBOX_STATUS_EPN_EP4 (0x05 << 29)
+#define RXMBOX_STATUS_EPN_EP6 (0x07 << 29)
+#define RXMBOX_STATUS_CORRUPTION (0x01 << 25)
+#define RXMBOX_STATUS_IBUS_ERROR (0x01 << 24)
+#define RXMBOX_STATUS_SETUP_MASK (0x01 << 23)
+#define RXMBOX_STATUS_SETUP_SHIFT 23
+#define RXMBOX_STATUS_SETUP_NORMAL (0x00 << 23)
+#define RXMBOX_STATUS_SETUP_SETUP (0x01 << 23)
+#define RXMBOX_STATUS_OVERRUN (0x01 << 22)
+#define RXMBOX_STATUS_DATA_TOGGLE (0x01 << 21)
+#define RXMBOX_STATUS_CRC (0x01 << 20)
+#define RXMBOX_STATUS_BIT_STUFFING (0x01 << 19)
+#define RXMBOX_STATUS_64K (0x01 << 18)
+#define RXMBOX_STATUS_MODE_MASK (0x03 << 16)
+#define RXMBOX_STATUS_MODE_SHIFT 16
+#define RXMBOX_STATUS_MODE_NORMAL (0x00 << 16)
+#define RXMBOX_STATUS_MODE_NORMAL2 (0x01 << 16)
+#define RXMBOX_STATUS_MODE_ASSEMBLE (0x02 << 16)
+#define RXMBOX_STATUS_MODE_SEPARATE (0x03 << 16)
+#define RXMBOX_STATUS_SIZE_MASK (0x0FFFF << 0)
+#define RXMBOX_STATUS_SIZE_SHIFT 0
+
+
+// ----------------------------------------------------------------------------
+// Hardware work around - see NEC erratum S1, CPU to IBUS write restriction.
+// Reading back from the USB device after every write prevents any problems.
+// Strictly speaking it is only necessary to do this after every three
+// writes, but if there is concurrent ethernet activity then doing it
+// after eveyr write is safer. The frame number/version register seems
+// like a good one to read back from.
+
+#ifdef CYGIMP_DEVS_USB_UPD985XX_IBUS_WRITE_LIMIT
+# define FLUSH_IBUS() \
+ CYG_MACRO_START \
+ (void)*USBS_VER; \
+ CYG_MACRO_END
+
+#else
+# define FLUSH_IBUS() CYG_EMPTY_STATEMENT
+#endif
+
+// ----------------------------------------------------------------------------
+// Static data. There is a data structure for each endpoint. The
+// implementation is essentially a private class that inherits from
+// common classes for control and data endpoints, but device drivers
+// are supposed to be written in C so some ugliness is required.
+//
+// Devtab entries are defined in usbs_upd985xx_data.cxx to make sure
+// that the linker does not garbage-collect them.
+
+// Support for the interrupt handling code.
+static cyg_interrupt usbs_upd985xx_intr_data;
+static cyg_handle_t usbs_upd985xx_intr_handle;
+
+// The various bits in the two interrupt status registers are read-once,
+// i.e. reading the register clears the bits. Since much of the processing
+// is deferred to DSR level, it is necessary to keep track of pending
+// interrupts in separate variables. If another interrupt happens during
+// DSR processing, these variables will be updated. The main DSR loops
+// until there are no interesting bits left. Interrupts have to be
+// disabled briefly when clearing bits.
+static volatile cyg_uint32 usbs_upd985xx_gsr1 = 0;
+static volatile cyg_uint32 usbs_upd985xx_gsr2 = 0;
+
+// Many of the interrupt bits are of no interest and it is convenient
+// to mask them out in the ISR, thus avoiding unnecessary dsr
+// invocations.
+static cyg_uint32 usbs_upd985xx_gsr1_mask = 0;
+static cyg_uint32 usbs_upd985xx_gsr2_mask = 0;
+
+// Sizes for the receive and transmit mboxes.
+// NOTE: it is not clear what the optimal size for these
+// mailboxes is. For receives maybe one per rx endpoint,
+// plus a spare. For transmits maybe just two, since only
+// one transmit at a time is supported. Mailboxes are
+// relatively small, so for now four each should be ok.
+#define RXMBOX_COUNT 4
+#define TXMBOX_COUNT 4
+
+// There is one instance of this data structure. It is allocated
+// in kseg0 cached memory, but during initialization a separate
+// pointer value is set to the kseg1 uncached equivalent. This
+// makes it easier to point the hardware at uncached memory without
+// having to worry about cache line boundaries everywhere.
+
+typedef struct uncached_data {
+ // This partial cacheline does not actually store any data.
+ // However it ensures that the data does not share a cacheline
+ // with some other static, with updates to that other static
+ // causing funny side effects on the uncached data. There is a
+ // memory optimisation of subtracting sizeof(RxMailbox.status),
+ // i.e. exploit knowledge of alignment.
+ unsigned char cacheline_start[HAL_DCACHE_LINE_SIZE - sizeof(cyg_uint32)];
+
+ RxMailbox rx_mboxes[RXMBOX_COUNT];
+ TxMailbox tx_mboxes[TXMBOX_COUNT];
+
+ // For transmits a single buffer descriptor per endpoint suffices.
+ // If transmit locking is enabled then actually a single buffer
+ // descriptor for the whole system would suffice.
+ TxBufferDescriptor ep0_tx_bufdesc;
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+ TxBufferDescriptor ep3_tx_bufdesc;
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+ TxBufferDescriptor ep5_tx_bufdesc;
+#endif
+
+
+ // More buffer descriptors are needed than might be expected, see
+ // the start_rx routines.
+ RxBufferDescriptor ep0_rx_bufdescs[4];
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
+ RxBufferDescriptor ep4_rx_bufdescs[8];
+#endif
+
+
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
+ // Space for the start and end of a transfer, avoiding problems
+ // with invalidating partial cache lines.
+ unsigned char ep4_head[HAL_DCACHE_LINE_SIZE];
+ unsigned char ep4_tail[HAL_DCACHE_LINE_SIZE];
+#endif
+
+ // The "big" buffers come last, reducing the offsets for the previous
+ // structures. It is not clear this really matters for MIPS.
+ //
+ // Endpoint 0 receive and transmit buffers. A transmit buffer is
+ // convenient because the hardware pretty much expects all of the
+ // data to be in contiguous memory, as opposed to the normal eCos
+ // USB driver model with refill buffers etc. An alternative
+ // implementation would keep the data in separate areas but would
+ // require lots of TxBufferDescriptors, so in memory terms the
+ // overheads of a single transmit buffer are not as big as might
+ // seem. It might be possible to get things working eight bytes
+ // at a time since the hardware appears to depend on zero-byte
+ // terminating packets in places, but that has not been attempted.
+ //
+ // A separate receive buffer is useful because it can be placed in
+ // uncached memory, avoiding the need for invalidation and
+ // worrying about other data in the cache lines. Note that this
+ // buffer may also get used for endpoint 6 interrupt receives
+ // because the two endpoints share a single pool.
+ unsigned char ep0_rx_buffer[CYGNUM_DEVS_USB_UPD985XX_EP0_RXBUFSIZE];
+ unsigned char ep0_tx_buffer[CYGNUM_DEVS_USB_UPD985XX_EP0_TXBUFSIZE];
+
+ // Another cacheline to prevent overlap with other statics.
+ // This has to be full-sized since the previous field is only byte-aligned.
+ unsigned char cacheline_end[HAL_DCACHE_LINE_SIZE];
+} uncached_data;
+
+// This data structure is quite large so making it all uninitialized
+// means a potentially big saving in ROM-booting systems. This
+// requires additional effort by the endpoint initialization routines.
+static uncached_data cached_copy;
+
+static uncached_data* uncached = (uncached_data*)0;
+
+// Endpoint 0. See the description below.
+
+static void usbs_upd985xx_ep0_start(usbs_control_endpoint*);
+static void usbs_upd985xx_poll(usbs_control_endpoint*);
+
+typedef struct ep0_impl {
+ usbs_control_endpoint common;
+ cyg_bool rx_expecting_data;
+ cyg_bool rx_indicator_valid;
+ RxMailbox rx_indicator;
+ cyg_bool tx_indicator_valid;
+ TxMailbox tx_indicator;
+ cyg_bool tx_needs_zero_transfer;
+ cyg_uint32 tx_size;
+} ep0_impl;
+
+static ep0_impl ep0 = {
+ common:
+ {
+ state: USBS_STATE_POWERED, // The hardware does not distinguish between detached, attached and powered.
+ enumeration_data: (usbs_enumeration_data*) 0,
+ start_fn: &usbs_upd985xx_ep0_start,
+ poll_fn: &usbs_upd985xx_poll,
+ interrupt_vector: CYGNUM_HAL_INTERRUPT_USB,
+ control_buffer: { 0, 0, 0, 0, 0, 0, 0, 0 },
+ state_change_fn: (void (*)(usbs_control_endpoint*, void*, usbs_state_change, int)) 0,
+ state_change_data: (void*) 0,
+ standard_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
+ standard_control_data: (void*) 0,
+ class_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
+ class_control_data: (void*) 0,
+ vendor_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
+ vendor_control_data: (void*) 0,
+ reserved_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
+ reserved_control_data: (void*) 0,
+ buffer: (unsigned char*) 0,
+ buffer_size: 0,
+ fill_buffer_fn: (void (*)(usbs_control_endpoint*)) 0,
+ fill_data: (void*) 0,
+ fill_index: 0,
+ complete_fn: (usbs_control_return (*)(usbs_control_endpoint*, int)) 0
+ },
+ rx_expecting_data: false,
+ rx_indicator_valid: false,
+ rx_indicator: { 0, (void*) 0 },
+ tx_indicator_valid: false,
+ tx_indicator: { 0 },
+ tx_needs_zero_transfer: 0
+};
+
+extern usbs_control_endpoint usbs_upd985xx_ep0 __attribute__((alias ("ep0")));
+
+// Endpoint 1, isochronous transmits. This endpoint is not yet
+// supported. Although the interface for bulk transmits should be
+// mostly re-usable, there are some additional error conditions if
+// either the host or the target fails to achieve the desired
+// throughput.
+
+// Endpoint 2, isochronous receives. Not yet supported for now, just
+// like endpoint 1.
+
+// Endpoints 3 and 5 can share some code.
+#if defined(CYGPKG_DEVS_USB_UPD985XX_EP3) || defined(CYGPKG_DEVS_USB_UPD985XX_EP5)
+// Endpoint 3, bulk transmits, and endpoint 5, either interrupt transmits
+// or emulation of bulk transmits. The hardware does most
+// of the work.
+typedef struct ep35_impl {
+ usbs_tx_endpoint common;
+ cyg_bool tx_indicator_valid;
+ TxMailbox tx_indicator;
+ int send_command;
+ volatile cyg_uint32* cr;
+ TxBufferDescriptor* tx_bufdesc;
+} ep35_impl;
+
+# ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+static void ep3_start_tx(usbs_tx_endpoint*);
+static void ep3_set_halted(usbs_tx_endpoint*, cyg_bool);
+
+static ep35_impl ep3 = {
+ common: {
+ start_tx_fn: &ep3_start_tx,
+ set_halted_fn: &ep3_set_halted,
+ complete_fn: (void (*)(void*, int)) 0,
+ complete_data: (void*) 0,
+ buffer: (const unsigned char*) 0,
+ buffer_size: 0,
+ halted: 0,
+ },
+ tx_indicator_valid: false,
+ tx_indicator: { 0 },
+ send_command: USBS_CMR_COMMAND_TX_EP3,
+ cr: EP3_CR,
+ tx_bufdesc: 0 // Needs run-time initialization
+};
+
+extern usbs_tx_endpoint usbs_upd985xx_ep3 __attribute__ ((alias ("ep3")));
+# endif
+
+# ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+static void ep5_start_tx(usbs_tx_endpoint*);
+static void ep5_set_halted(usbs_tx_endpoint*, cyg_bool);
+
+static ep35_impl ep5 = {
+ common: {
+ start_tx_fn: &ep5_start_tx,
+ set_halted_fn: &ep5_set_halted,
+ complete_fn: (void (*)(void*, int)) 0,
+ complete_data: (void*) 0,
+ buffer: (const unsigned char*) 0,
+ buffer_size: 0,
+ halted: 0,
+ },
+ tx_indicator_valid: false,
+ tx_indicator: { 0 },
+ send_command: USBS_CMR_COMMAND_TX_EP5,
+ cr: EP5_CR,
+ tx_bufdesc: 0 // Needs run-time initialization
+};
+
+extern usbs_tx_endpoint usbs_upd985xx_ep5 __attribute__ ((alias ("ep5")));
+# endif
+#endif
+
+
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
+// Endpoint 4, bulk receives. Again the hardware does the hard work.
+// Receive pool 2 is reserved for this endpoint.
+
+typedef struct ep4_impl {
+ usbs_rx_endpoint common;
+ cyg_uint32 head_size;
+ cyg_uint32 direct_size;
+ cyg_uint32 tail_size;
+ cyg_bool rx_indicator_valid;
+ RxMailbox rx_indicator;
+ cyg_int32 tail_index;
+} ep4_impl;
+
+static void ep4_start_rx(usbs_rx_endpoint*);
+static void ep4_set_halted(usbs_rx_endpoint*, cyg_bool);
+
+static ep4_impl ep4 = {
+ common: {
+ start_rx_fn: &ep4_start_rx,
+ set_halted_fn: &ep4_set_halted,
+ complete_fn: (void (*)(void*, int)) 0,
+ complete_data: (void*) 0,
+ buffer: (unsigned char*) 0,
+ buffer_size: 0,
+ halted: 0,
+ },
+ rx_indicator_valid: false,
+ rx_indicator: { 0, (void*) 0 },
+ tail_index: -1
+};
+
+extern usbs_rx_endpoint usbs_upd985xx_ep4 __attribute__((alias ("ep4")));
+#endif
+
+// Endpoint 6, interrupt receives. Not yet implemented. There may
+// be conflicts because the hardware is shared with endpoint 0.
+
+// ----------------------------------------------------------------------------
+// Mailbox support.
+//
+// The transmit and receive mailboxes are shared between the
+// appropriate endpoints. This causes some complications if e.g.
+// transmits on several endpoints complete at the same time. For
+// example the tx mailbox might contain send indicators for endpoints
+// 3 and 0, but the DSR code will process endpoint 0 before endpoint
+// 3.
+//
+// This device driver works on the basis that there can be only one
+// transmit and/or receive in progress for any given endpoint, so the
+// relevant information can be extracted from the mailbox and put into
+// the per-endpoint structures. The routines below can be used to
+// move data from the mailboxes. They will be called in DSR context
+// so there is no need to worry about locking.
+
+static void
+drain_tx_mailbox(void)
+{
+ TxMailbox* tmra = IBUS_SWAPPTR(TxMailbox, *USBS_TMRA);
+ TxMailbox* tmwa = IBUS_SWAPPTR(TxMailbox, *USBS_TMWA);
+ if (tmra != tmwa) {
+ do {
+ TxMailbox mbox = *tmra;
+ tmra++;
+ if (tmra == &(uncached->tx_mboxes[TXMBOX_COUNT])) {
+ tmra = &(uncached->tx_mboxes[0]);
+ }
+
+ switch(mbox.status & TXMBOX_STATUS_EPN_MASK) {
+ case TXMBOX_STATUS_EPN_EP0:
+ CYG_ASSERT(false == ep0.tx_indicator_valid, "Only one ep0 transmit should be in progress at a time");
+ ep0.tx_indicator = mbox;
+ ep0.tx_indicator_valid = true;
+ break;
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+ case TXMBOX_STATUS_EPN_EP3:
+ CYG_ASSERT(false == ep3.tx_indicator_valid, "Only one ep3 transmit should be in progress at a time");
+ ep3.tx_indicator = mbox;
+ ep3.tx_indicator_valid = true;
+ break;
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+ case TXMBOX_STATUS_EPN_EP5:
+ CYG_ASSERT(false == ep5.tx_indicator_valid, "Only one ep5 transmit should be in progress at a time");
+ ep5.tx_indicator = mbox;
+ ep5.tx_indicator_valid = true;
+ break;
+#endif
+ default:
+ break;
+ }
+ } while (tmra != tmwa);
+ *USBS_TMRA = IBUS_SWAPPTR(TxMailbox, tmra); FLUSH_IBUS();
+ }
+}
+
+static void
+drain_rx_mailbox(void)
+{
+ RxMailbox* rmra = IBUS_SWAPPTR(RxMailbox, *USBS_RMRA);
+ RxMailbox* rmwa = IBUS_SWAPPTR(RxMailbox, *USBS_RMWA);
+
+ if (rmra != rmwa) {
+ do {
+ RxMailbox mbox = *rmra;
+ rmra++;
+ if (rmra == &(uncached->rx_mboxes[RXMBOX_COUNT])) {
+ rmra = &(uncached->rx_mboxes[0]);
+ }
+
+ switch(mbox.status & RXMBOX_STATUS_EPN_MASK) {
+ case RXMBOX_STATUS_EPN_EP0:
+ // Ignore zero-byte transfers. It is not clear why
+ // these happen, but they have been observed.
+ if (0 != (mbox.status & RXMBOX_STATUS_SIZE_MASK)) {
+ CYG_ASSERT(false == ep0.rx_indicator_valid, "Only one ep0 receive should be in progress at a time");
+ ep0.rx_indicator = mbox;
+ ep0.rx_indicator_valid = true;
+ }
+ break;
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
+ case RXMBOX_STATUS_EPN_EP4:
+ // If an error occurs then the hardware may report
+ // multiple rx completions, each with an IBUS error
+ // indicator. For now only the last rx indicator is
+ // taken into account, which means we could lose
+ // a successful receive that happens to be followed
+ // by an error.
+ // NOTE: any possibility of improving on this?
+#if 1
+ CYG_ASSERT(false == ep4.rx_indicator_valid, "Only one ep4 receive should be in progress at a time");
+#endif
+ ep4.rx_indicator = mbox;
+ ep4.rx_indicator_valid = true;
+ break;
+#endif
+ default:
+ break;
+ }
+ } while (rmra != rmwa);
+ *USBS_RMRA = IBUS_SWAPPTR(RxMailbox, rmra); FLUSH_IBUS();
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Transmit locking.
+//
+// According to NEC errata U3 and U4 the hardware may exhibit
+// undesirable behaviour if there are concurrent transmissions. There
+// are various ways of resolving this, but the simplest is to perform
+// locking in software so that at most one transmit endpoint is in use
+// at any one time. This approach works fine if transmissions only
+// involve one tx endpoint plus the control endpoint because the
+// control endpoint generally only gets used during initialization and
+// the other endpoint only gets used after initialization. If multiple
+// transmit endpoints are used then locking in software becomes less
+// acceptable, especially if isochronous transfers are used because
+// timing is important for those.
+//
+// There is a theoretical problem if e.g. there is a very large bulk
+// transfer on a busy bus and it is necessary to respond to a control
+// message. The control reply would be delayed, possibly causing a
+// violation of the USB standard and a timeout on the host.
+
+#ifdef CYGIMP_DEVS_USB_UPD985XX_SERIALIZE_TRANSMITS
+static void ep0_start_tx(void);
+# if defined(CYGPKG_DEVS_USB_UPD985XX_EP3) || defined(CYGPKG_DEVS_USB_UPD985XX_EP5)
+static void ep35_start_tx(ep35_impl*);
+# endif
+
+static cyg_bool tx_in_progress = false;
+static cyg_bool ep0_tx_pending = false;
+# ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+static cyg_bool ep3_tx_pending = false;
+# endif
+# ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+static cyg_bool ep5_tx_pending = false;
+# endif
+
+// Invoked from ep?_start_tx(). Scheduling may or may not be locked.
+static cyg_bool
+tx_try_lock(cyg_bool* which)
+{
+ cyg_bool result;
+ cyg_drv_dsr_lock();
+ if (tx_in_progress) {
+ result = false;
+ *which = true;
+ } else {
+ result = true;
+ tx_in_progress = true;
+ }
+ cyg_drv_dsr_unlock();
+ return result;
+}
+
+// Invoked only from dsr context.
+static void
+tx_unlock(void)
+{
+ tx_in_progress = false;
+ if (ep0_tx_pending) {
+ ep0_tx_pending = false;
+ ep0_start_tx();
+ return;
+ }
+# ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+ if (ep3_tx_pending) {
+ ep3_tx_pending = false;
+ ep35_start_tx(&ep3);
+ return;
+ }
+# endif
+# ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+ if (ep5_tx_pending) {
+ ep5_tx_pending = false;
+ ep35_start_tx(&ep5);
+ return;
+ }
+# endif
+}
+
+# define TX_TRY_LOCK(_x_) tx_try_lock(_x_)
+# define TX_UNLOCK() tx_unlock()
+
+#else
+
+# define TX_TRY_LOCK(_x_) 1
+# define TX_UNLOCK() CYG_EMPTY_STATEMENT
+
+#endif
+
+
+// ----------------------------------------------------------------------------
+// Endpoint 0
+//
+// As usual, control messages are more complicated than the rest.
+//
+// 1) during initialization a receive is initiated into the common
+// eight-byte buffer, used for the standard header of the control
+// packet. Until that header has been received and analysed,
+// there is no way of knowing whether or not the host will be
+// sending any more data.
+//
+// 2) the control packet may indicate that the host will be sending
+// more data. A higher-level handler for the control message should
+// have provided a suitable buffer, so a receive can be started
+// into that buffer. A flag indicates whether we are currently
+// receiving a new control packet or additional data.
+//
+// 3) the host may decide to cancel that extra data and send a new
+// control message instead. There is a flag to indicate that
+// the transfer included a SETUP token.
+//
+// 4) transmits only happen when the control packet involves returning
+// data. Unfortunately there is a problem in that, with eCos, the
+// return data will generally not be in a single contiguous buffer.
+// Discontinuous data could be handled by having a separate buffer
+// descriptor for each bit of data, but it is not known in advance
+// how many buffer descriptors might be needed so allocating
+// those statically presents a problem as well. Instead a single
+// static buffer is used, and data from higher-level code is copied
+// there. This introduces a new problem: how big should that buffer
+// be? A configuration option is used for that.
+//
+// If endpoint 6 is in use as well then things get more complicated
+// because a single receive pool will be shared between endpoints 0
+// and 6, and when adding a buffer to a pool there is no way of
+// specifying the endpoint. Hence it will be necessary to receive
+// into a static buffer and then copy into either an endpoint 0 or
+// and endpoint 6 buffer.
+
+// Fill the transmit buffer by repeatedly invoking the refill function
+// and copying into the ep0 tx buffer. The relevant fields in the
+// ep0 structure are cleared immediately and the completion function
+// is called, even though the data has not actually gone out. That avoids
+// a possible race condition where the host sends a new control packet
+// immediately, before the transmit-complete has been processed
+// (unlikely in practice, not least because ep0_tx_dsr() will get called
+// before ep0_rx_dsr()).
+static int
+ep0_fill_txbuffer(void)
+{
+ int filled = 0;
+ while (filled < CYGNUM_DEVS_USB_UPD985XX_EP0_TXBUFSIZE) {
+ if (0 != ep0.common.buffer_size) {
+ if ((filled + ep0.common.buffer_size) < CYGNUM_DEVS_USB_UPD985XX_EP0_TXBUFSIZE) {
+ memcpy(&(uncached->ep0_tx_buffer[filled]), ep0.common.buffer, ep0.common.buffer_size);
+ filled += ep0.common.buffer_size;
+ ep0.common.buffer_size = 0;
+ } else {
+ break;
+ }
+ } else if ((void (*)(usbs_control_endpoint*))0 != ep0.common.fill_buffer_fn) {
+ (*ep0.common.fill_buffer_fn)(&ep0.common);
+ } else {
+ break;
+ }
+ }
+ CYG_ASSERT((0 == ep0.common.buffer_size) && ((void (*)(usbs_control_endpoint*))0 == ep0.common.fill_buffer_fn), \
+ "Endpoint 0 transmit buffer overflow");
+
+ if ((usbs_control_return (*)(usbs_control_endpoint*, int))0 != ep0.common.complete_fn) {
+ (*ep0.common.complete_fn)(&ep0.common, 0);
+ }
+ ep0.common.buffer = (unsigned char*) 0;
+ ep0.common.buffer_size = 0;
+ ep0.common.fill_buffer_fn = 0;
+ ep0.common.complete_fn = 0;
+
+ return filled;
+}
+
+// Start a new receive operation on endpoint 0. This needs to happen
+// from a number of places, including from initialization.
+//
+// IMHO the hardware is somewhat overengineered here. All that is
+// needed is to receive a single eight-byte control packet, or a
+// small amount of additional control data. That could be achieved
+// by using a single buffer descriptor in the uncached structure,
+// plus a suitably-sized static uncached ep0_rx_buffer.
+//
+// But no, buffer descriptors must be linked and new buffers must
+// be added to the end. When a control packet arrives, the
+// receive pool continues to point at the old buffer descriptor.
+// So we need two buffer descriptors plus two links, switching
+// between them as appropriate.
+//
+// It is not at all clear what would happen if another packet
+// started to happen while things were being updated. There is
+// also potential confusion between endpoint 0 and endpoint 6
+// receives.
+
+static void
+ep0_start_rx(cyg_uint32 size)
+{
+ // The buffer descriptor to be added. This will be either
+ // ep0_rxbufdescs[0] or ep0_rxbufdescs[2];
+ RxBufferDescriptor* desc = &(uncached->ep0_rx_bufdescs[0]);
+
+ CYG_ASSERTC(size > 0);
+
+ // Block interrupts for the duration. This does not prevent
+ // problems if the hardware sees another packet and starts
+ // doing things, but should prevent some software race
+ // conditions.
+ cyg_drv_isr_lock();
+
+ // We are about to start a new rx operation, so the
+ // current indicator may get invalidated.
+ ep0.rx_indicator_valid = false;
+
+ // Start by looking at the current pool0 status. There are
+ // three possibilities: during init or after reset, the pool
+ // will be empty; otherwise the pool should point at either
+ // rx_bufdescs[0] or rx_bufdescs[2], corresponding to the
+ // last received packet.
+ if (0 == (*USBS_RP0IR & USBS_RPxIR_RNOD_MASK)) {
+ // Nothing currently in the pool. Use ep0_rx_bufdescs[0],
+ // and no need to update a link.
+ } else if (desc == *USBS_RP0AR) {
+ // The pool already points at bufdescs[0], switch to bufdescs[2],
+ // and link from bufdescs[1].
+ desc = &(uncached->ep0_rx_bufdescs[2]);
+ uncached->ep0_rx_bufdescs[1].buffer = (void*) desc;
+ } else {
+ // The pool should point at bufdescs[2], stick with bufdescs[0]
+ CYG_ASSERT(&(uncached->ep0_rx_bufdescs[2]) == *USBS_RP0AR, "Endpoint 0 rx buffer confusion");
+ uncached->ep0_rx_bufdescs[3].buffer = (void*) desc;
+ }
+
+ // Now fill in the buffer directory being added
+ desc[0].control = RXBUFDESC_CTRL_LAST | RXBUFDESC_CTRL_BUFDESC_BUFDESC | size;
+ desc[0].buffer = (void*) uncached->ep0_rx_buffer;
+ desc[1].control = RXBUFDESC_CTRL_BUFDESC_LINK;
+ desc[1].buffer = 0;
+
+ while (0 != (*USBS_CMR & IBUS_SWAP32(USBS_CMR_BUSY))) {
+ // Do nothing: this situation should be short-lived.
+ }
+ *USBS_CA = IBUS_SWAPPTR(void, desc); FLUSH_IBUS();
+ *USBS_CMR = IBUS_SWAP32(USBS_CMR_COMMAND_ADD_POOL0 | 1); FLUSH_IBUS();
+ cyg_drv_isr_unlock();
+}
+
+// Ditto for transmits. The data is assumed to be in
+// uncached->ep0_tx_buffer already. A size of 0 indicates
+// a need to send a terminating packet explicitly.
+static void
+ep0_start_tx(void)
+{
+ if (!TX_TRY_LOCK(&ep0_tx_pending)) {
+ return;
+ }
+
+ uncached->ep0_tx_bufdesc.buffer = uncached->ep0_tx_buffer;
+ uncached->ep0_tx_bufdesc.control = TXBUFDESC_CTRL_LAST | TXBUFDESC_CTRL_BUFDESC_BUFDESC | ep0.tx_size;
+
+ cyg_drv_isr_lock();
+ while (0 != (*USBS_CMR & IBUS_SWAP32(USBS_CMR_BUSY))) {
+ // Do nothing: this situation should be short-lived.
+ }
+ *USBS_CA = IBUS_SWAPPTR(void, &(uncached->ep0_tx_bufdesc)); FLUSH_IBUS();
+ *USBS_CMR = IBUS_SWAP32(USBS_CMR_COMMAND_TX_EP0 | ep0.tx_size); FLUSH_IBUS();
+ cyg_drv_isr_unlock();
+}
+
+// An endpoint 0 transmission has completed. Usually the only action
+// that is needed is to drain the tx mailbox entry, otherwise it is
+// possible that we could end up with ep0 transmits using up all
+// available slots. The endpoint 0 hardware requires no further
+// attention, and as far as higher-level code is concerned the
+// transmission completed a long time ago when ep0_fill_txbuffer()
+// called the completion function.
+//
+// There is one special case. If the host asked for e.g. a string
+// descriptor and asked for 255 bytes, but the string was only
+// e.g. 32 bytes, then there is a problem. With a default value
+// for CYGNUM_DEVS_USB_UPD985XX_EP0_PKTSIZE, the data will be
+// transferred as four 8-byte packets, but it is necessary to
+// terminate the transfer with a 0-byte packet. Endpoint 0 always
+// operates in NZLP mode so the hardware will never generate
+// this last packet. Instead it is necessary to set up an
+// additional transfer of zero bytes. That could be done at the
+// same time as the main data transfer, but then it would be
+// necessary to poll the hardware and wait until it has finished
+// processing that initial transfer.
+static void
+ep0_tx_dsr(void)
+{
+ if (!ep0.tx_indicator_valid) {
+ drain_tx_mailbox();
+ if (!ep0.tx_indicator_valid) {
+ // A transmit interrupt when there does not appear to be
+ // any data?
+ CYG_FAIL("EP0 tx DSR invoked when there is no valid tx indicator");
+ return;
+ }
+ }
+ // There is not actually anything worth looking at in the status.
+ ep0.tx_indicator_valid = false;
+
+ if (ep0.tx_needs_zero_transfer) {
+ ep0.tx_needs_zero_transfer = false;
+ uncached->ep0_tx_bufdesc.buffer = uncached->ep0_tx_buffer;
+ uncached->ep0_tx_bufdesc.control = TXBUFDESC_CTRL_LAST | TXBUFDESC_CTRL_BUFDESC_BUFDESC | 0;
+
+ cyg_drv_isr_lock();
+ while (0 != (*USBS_CMR & IBUS_SWAP32(USBS_CMR_BUSY))) {
+ // Do nothing: this situation should be short-lived.
+ }
+ *USBS_CA = IBUS_SWAPPTR(void, &(uncached->ep0_tx_bufdesc)); FLUSH_IBUS();
+ *USBS_CMR = IBUS_SWAP32(USBS_CMR_COMMAND_TX_EP0 | 0); FLUSH_IBUS();
+ cyg_drv_isr_unlock();
+
+ } else {
+ TX_UNLOCK();
+ }
+}
+
+// An endpoint 0 receive has completed. This could be a new control
+// message. Or it could be the data for a previous control message. Or
+// it could be a new control message when expecting the data from
+// a previous one. The ep0.rx_expecting_data field indicates
+// whether or not a new control message is expected.
+//
+// At times an interrupt triggers and there is an rx indication for a
+// zero-byte transfer. Such a transfer may be followed immediately by
+// a real transfer. It is not understood why the zero-byte transfer
+// occurs. They are ignored by the drain_rx_mailbox() code, to make
+// sure that there is at most one valid rx indicator at a time.
+static void
+ep0_rx_dsr(void)
+{
+ // Start by checking the rx indicator to make sure that a packet
+ // really has been received.
+ if (!ep0.rx_indicator_valid) {
+ drain_rx_mailbox();
+ if (!ep0.rx_indicator_valid) {
+ // Do not assert, in case of a spurious interrupt for a
+ // zero-byte transfer.
+ return;
+ }
+ }
+
+ // We have a valid receive, with the data held in uncached->ep0_rx_buffer.
+ // Are we expecting the remaining data of a control transfer?
+ if (ep0.rx_expecting_data) {
+ // Was this data interrupted by a new setup packet?
+ if (0 != (ep0.rx_indicator.status & RXMBOX_STATUS_SETUP_SETUP)) {
+ // NOTE: it is not clear from the documentation exactly what
+ // happens here, e.g. is it guaranteed that the new control
+ // packet appears at the start of the buffer rather than
+ // after any data previously received? Given typical
+ // USB host-side implementations this scenario is considered
+ // sufficiently unlikely that no further investigation has
+ // been carried out.
+
+ // Inform higher-level code that the receive has been aborted.
+ if ((usbs_control_return (*)(usbs_control_endpoint*, int)) 0 != ep0.common.complete_fn) {
+ (*ep0.common.complete_fn)(&ep0.common, -EIO);
+ }
+ ep0.rx_expecting_data = false;
+ ep0.common.buffer = (unsigned char*) 0;
+ ep0.common.buffer_size = 0;
+ ep0.common.fill_buffer_fn = 0;
+ ep0.common.complete_fn = (usbs_control_return (*)(usbs_control_endpoint*, int)) 0;
+ // Fall through the main control message handling code below.
+ } else {
+ // Data was expected and received. Transfer the data to the
+ // user's buffer, and perform completion.
+ usbs_control_return result;
+ cyg_uint32 size = ep0.rx_indicator.status & RXMBOX_STATUS_SIZE_MASK;
+
+ CYG_ASSERT( (usbs_control_return (*)(usbs_control_endpoint*, int))0 != ep0.common.complete_fn, \
+ "A completion function should be provided for OUT control messages");
+ CYG_ASSERT(size == ep0.common.buffer_size, "Inconsistency between buffer and transfer sizes");
+ memcpy(ep0.common.buffer, uncached->ep0_rx_buffer, size);
+ result = (*ep0.common.complete_fn)(&ep0.common, 0);
+ ep0.common.buffer = (unsigned char*) 0;
+ ep0.common.buffer_size = 0;
+ ep0.common.complete_fn = (usbs_control_return (*)(usbs_control_endpoint*, int)) 0;
+ ep0.rx_expecting_data = false;
+
+ // Start another receive for the next control message.
+ // Note that there has been a window where there was no receive
+ // in progress for endpoint 0, even though according to the
+ // USB spec a device must always be able to accept new
+ // control messages.
+ ep0_start_rx(8);
+ return;
+ }
+ }
+
+ // When we get here we should have an eight-byte control message
+ // in uncached->ep0_rx_buffer. This should get moved into
+ // the ep0.common.control_buffer so that higher-level code sees
+ // it in the appropriate location.
+ CYG_ASSERT((ep0.rx_indicator.address == &(uncached->ep0_rx_bufdescs[0])) || \
+ (ep0.rx_indicator.address == &(uncached->ep0_rx_bufdescs[2])), \
+ "Received ep0 data should involve the ep0 rx buffer descriptor");
+
+ CYG_ASSERT(8 == (ep0.rx_indicator.status & RXMBOX_STATUS_SIZE_MASK), "Control messages should be 8 bytes");
+ memcpy(ep0.common.control_buffer, uncached->ep0_rx_buffer, 8);
+
+ // If we have received a control packet then any reset signals really
+ // will have come from the host and must be processed normally.
+ // Make sure that reset interrupts are no longer masked off.
+ if (0 == (*USBS_IMR2 & IBUS_SWAP32(USBS_GSR2_URST))) {
+ *USBS_IMR2 |= IBUS_SWAP32(USBS_GSR2_URST); FLUSH_IBUS();
+ usbs_upd985xx_gsr2_mask |= USBS_GSR2_URST;
+ }
+
+ {
+ usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+ usb_devreq* req = (usb_devreq*) ep0.common.control_buffer;
+ int length, direction, protocol, recipient;
+
+ // Now we need to do some decoding of the data. A non-zero
+ // length field indicates that there will be a subsequent
+ // IN or OUT phase. The direction is controlled by the
+ // top bit of the first byte. The protocol is determined
+ // by other bits of the top byte.
+ length = (req->length_hi << 8) | req->length_lo;
+ direction = req->type & USB_DEVREQ_DIRECTION_MASK;
+ protocol = req->type & USB_DEVREQ_TYPE_MASK;
+ recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
+
+ DBG(("ep0, new control request: type %x, code %x\n", req->type, req->request));
+ DBG((" %s, length %d, value hi %x lo %x, index hi %x lo %x\n",
+ (USB_DEVREQ_DIRECTION_OUT == direction) ? "out" : "in",
+ length, req->value_hi, req->value_lo, req->index_hi, req->index_lo));
+
+ if (USB_DEVREQ_TYPE_STANDARD == protocol) {
+
+ // First see if the request can be handled entirely in
+ // this module.
+ if (USB_DEVREQ_SET_ADDRESS == req->request) {
+ // The USB device address should be in value_lo.
+ // No more data is expected.
+ int old_state = ep0.common.state;
+ int address = req->value_lo;
+ if ((0 != length) || (address > 127)) {
+ result = USBS_CONTROL_RETURN_STALL;
+ } else {
+ *USBS_GMR = (*USBS_GMR & ~(USBS_GMR_FA_MASK | USBS_GMR_VT)) | (address << USBS_GMR_FA_SHIFT); FLUSH_IBUS();
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+ // Switch to addressed state, informing higher-level
+ // code of this.
+ if (USBS_STATE_ADDRESSED != (old_state & USBS_STATE_MASK)) {
+ ep0.common.state = USBS_STATE_ADDRESSED;
+ if ((void (*)(usbs_control_endpoint*, void*, usbs_state_change, int))0 != ep0.common.state_change_fn) {
+ (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+ USBS_STATE_CHANGE_ADDRESSED, old_state);
+ }
+ }
+ // End of SET_ADDRESS handling
+ } else if (USB_DEVREQ_GET_STATUS == req->request) {
+ // GET_STATUS on the device as a whole is used to
+ // check the remote-wakeup and self-powered bits.
+ // GET_STATUS on an endpoint is used to determine
+ // the halted condition.
+ // GET_STATUS on anything else has to be left to
+ // other code.
+ if (USB_DEVREQ_RECIPIENT_DEVICE == recipient) {
+ // The host should expect two bytes back.
+ if ((2 == length) && (USB_DEVREQ_DIRECTION_IN == direction)) {
+ ep0.common.control_buffer[0] = 0; // Not self-powered, no remote wakeup
+ ep0.common.control_buffer[1] = 0;
+ ep0.common.buffer = ep0.common.control_buffer;
+ ep0.common.buffer_size = 2;
+ result = USBS_CONTROL_RETURN_HANDLED;
+ } else {
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+
+ } else if (USB_DEVREQ_RECIPIENT_ENDPOINT == recipient) {
+ if ((2 == length) && (USB_DEVREQ_DIRECTION_IN == direction)) {
+ int endpoint = req->index_lo;
+ if (0 == endpoint) {
+ // get-status on endpoint 0 is either undefined or always valid.
+ // endpoint 0 is always up.
+ ep0.common.control_buffer[0] = 0;
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+ else if (((USB_DEVREQ_INDEX_DIRECTION_IN | 3) == endpoint) &&
+ (USBS_STATE_CONFIGURED == (ep0.common.state & USBS_STATE_MASK))) {
+ ep0.common.control_buffer[0] = ep3.common.halted;
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
+ else if (((USB_DEVREQ_INDEX_DIRECTION_OUT | 4) == endpoint) &&
+ (USBS_STATE_CONFIGURED == (ep0.common.state & USBS_STATE_MASK))) {
+ ep0.common.control_buffer[0] = ep4.common.halted;
+ result = USBS_CONTROL_RETURN_HANDLED;
+
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+ else if (((USB_DEVREQ_INDEX_DIRECTION_IN | 5) == endpoint) &&
+ (USBS_STATE_CONFIGURED == (ep0.common.state & USBS_STATE_MASK))) {
+ ep0.common.control_buffer[0] = ep5.common.halted;
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#endif
+ else {
+ // An invalid endpoint has been specified or the
+ // endpoint can only be examined in configured state.
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+ if (USBS_CONTROL_RETURN_HANDLED == result) {
+ ep0.common.control_buffer[1] = 0;
+ ep0.common.buffer = ep0.common.control_buffer;
+ ep0.common.buffer_size = 2;
+ }
+ } else {
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+ } // Endpoint or device get-status
+
+ } else if (USB_DEVREQ_CLEAR_FEATURE == req->request) {
+
+ // CLEAR_FEATURE operates in much the same way as
+ // GET_STATUS
+ if (USB_DEVREQ_RECIPIENT_DEVICE == recipient) {
+
+ // No data should be transferred, and only remote-wakeup can be cleared.
+ if ((0 != length) || (USB_DEVREQ_FEATURE_DEVICE_REMOTE_WAKEUP != req->value_lo)) {
+ result = USBS_CONTROL_RETURN_STALL;
+ } else {
+ // Clearing remote-wakeup is a no-op.
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+
+ } else if (USB_DEVREQ_RECIPIENT_ENDPOINT == recipient) {
+ // The only feature that can be cleared is endpoint-halt, no data should be transferred.
+ if ((0 != length) || (USB_DEVREQ_FEATURE_ENDPOINT_HALT != req->value_lo)) {
+ result = USBS_CONTROL_RETURN_STALL;
+ } else {
+ int endpoint = req->index_lo;
+ if (0 == endpoint) {
+ // Clearing halt on endpoint 0 is always a no-op since that endpoint cannot be halted
+ }
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+ else if (((USB_DEVREQ_INDEX_DIRECTION_IN | 3) == endpoint) &&
+ (USBS_STATE_CONFIGURED == (ep0.common.state & USBS_STATE_MASK))) {
+ ep3_set_halted(&ep3.common, false);
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
+ else if (((USB_DEVREQ_INDEX_DIRECTION_OUT | 4) == endpoint) &&
+ (USBS_STATE_CONFIGURED == (ep0.common.state & USBS_STATE_MASK))) {
+ ep4_set_halted(&ep4.common, false);
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+ else if (((USB_DEVREQ_INDEX_DIRECTION_IN | 5) == endpoint) &&
+ (USBS_STATE_CONFIGURED == (ep0.common.state & USBS_STATE_MASK))) {
+ ep5_set_halted(&ep5.common, false);
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#endif
+ else {
+ // Invalid endpoint or not in configured state.
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+ }
+ } // Endpoing or device clear-feature
+
+ } else if (USB_DEVREQ_SET_FEATURE == req->request) {
+
+ // SET_FEATURE also operates in much the same way as
+ // GET_STATUS
+ if (USB_DEVREQ_RECIPIENT_DEVICE == recipient) {
+ // The only valid feature that can be set is remote-wakeup,
+ // which is not supported by this driver.
+ result = USBS_CONTROL_RETURN_STALL;
+
+ } else if (USB_DEVREQ_RECIPIENT_ENDPOINT == recipient) {
+
+ // Only the halt condition can be set, and no data should be transferred.
+ // Halting endpoint 0 should probably be disallowed although the
+ // standard does not explicitly say so.
+ if ((0 != length) ||
+ (USB_DEVREQ_FEATURE_ENDPOINT_HALT != req->value_lo) ||
+ (USBS_STATE_CONFIGURED != (ep0.common.state & USBS_STATE_MASK))) {
+
+ result = USBS_CONTROL_RETURN_STALL;
+
+ } else {
+ int endpoint = req->index_lo;
+ if (0) {
+ }
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+ else if ((USB_DEVREQ_INDEX_DIRECTION_IN | 3) == endpoint) {
+ ep3_set_halted(&ep3.common, true);
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
+ else if ((USB_DEVREQ_INDEX_DIRECTION_OUT | 4) == endpoint) {
+ ep4_set_halted(&ep4.common, true);
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+ else if ((USB_DEVREQ_INDEX_DIRECTION_IN | 5) == endpoint) {
+ ep5_set_halted(&ep5.common, true);
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#endif
+ else {
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+ }
+ } // Endpoint or device set-feature
+ }
+
+ // If the result has not been handled yet, pass it to
+ // the installed callback function (if any).
+ if (USBS_CONTROL_RETURN_UNKNOWN == result) {
+ if ((usbs_control_return (*)(usbs_control_endpoint*, void*))0 != ep0.common.standard_control_fn) {
+ result = (*ep0.common.standard_control_fn)(&ep0.common, ep0.common.standard_control_data);
+ }
+ }
+
+ // If the result has still not been handled, leave it to
+ // the default implementation in the USB slave common place.
+ if (USBS_CONTROL_RETURN_UNKNOWN == result) {
+ result = usbs_handle_standard_control(&ep0.common);
+ }
+ } else {
+ // The other three types of control message can be
+ // handled by similar code.
+ usbs_control_return (*callback_fn)(usbs_control_endpoint*, void*);
+ void* callback_arg;
+
+ if (USB_DEVREQ_TYPE_CLASS == protocol) {
+ callback_fn = ep0.common.class_control_fn;
+ callback_arg = ep0.common.class_control_data;
+ } else if (USB_DEVREQ_TYPE_VENDOR == protocol) {
+ callback_fn = ep0.common.vendor_control_fn;
+ callback_arg = ep0.common.vendor_control_data;
+ } else {
+ callback_fn = ep0.common.reserved_control_fn;
+ callback_arg = ep0.common.reserved_control_data;
+ }
+
+ if ((usbs_control_return (*)(usbs_control_endpoint*, void*)) 0 == callback_fn) {
+ result = USBS_CONTROL_RETURN_STALL;
+ } else {
+ result = (*callback_fn)(&ep0.common, callback_arg);
+ }
+ }
+
+ if (USBS_CONTROL_RETURN_HANDLED != result) {
+ // This control request cannot be handled. Generate a stall.
+ // These stalls will be cleared automaticaly by the next
+ // setup packet.
+ *EP0_CR |= (EP0_CR_ISS | EP0_CR_OSS); FLUSH_IBUS();
+ // Start a receive for the next control message
+ ep0_start_rx(8);
+ } else {
+ // The control request has been handled. Is there any more
+ // data to be transferred?
+ if (0 == length) {
+ // Definitely start a receive for another control message
+ ep0_start_rx(8);
+
+ // This operation is complete so we need to ack. It
+ // appears that the way to achieve this is to send a
+ // zero-byte packet.
+ ep0.tx_size = 0;
+ ep0_start_tx();
+
+ } else {
+ // Time to check the direction.
+
+ if (USB_DEVREQ_DIRECTION_OUT == direction) {
+ // The host expects to send more data. Higher-level code
+ // should have provided an appropriate buffer.
+ CYG_ASSERT( (unsigned char*) 0 != ep0.common.buffer, "A receive buffer should have been provided");
+ CYG_ASSERT( (usbs_control_return (*)(usbs_control_endpoint*, int))0 != ep0.common.complete_fn, \
+ "A completion function should be provided for OUT control messages");
+ CYG_ASSERT(length <= CYGNUM_DEVS_USB_UPD985XX_EP0_RXBUFSIZE, "Insufficient buffer space configured");
+
+ ep0.rx_expecting_data = true;
+ ep0_start_rx(length);
+ } else {
+ // The host expects to be able to read some data.
+ // This needs to go into a single contiguous
+ // buffer, and then the transfer can be started.
+ // Care has to be taken with various boundary conditions.
+ int actual_length = ep0_fill_txbuffer();
+ if (actual_length > length) {
+ actual_length = length;
+ }
+ if ((length != actual_length) && (0 == (actual_length % CYGNUM_DEVS_USB_UPD985XX_EP0_PKTSIZE))) {
+ ep0.tx_needs_zero_transfer = true;
+ } else {
+ ep0.tx_needs_zero_transfer = false;
+ }
+ ep0.tx_size = actual_length;
+ ep0_start_tx();
+
+ // And make sure that there is another receive in progress
+ // for the next setup packet.
+ ep0_start_rx(8);
+ }
+ }
+ } // Control message handled
+ }
+}
+
+// Endpoint 0 initialization also takes care of initializing generic bits
+// of the USB controller, for example letting through resume and suspend
+// interrupts and setting up the mailboxes. Also, it is necessary to
+// start a receive operation so that the first control message can
+// be processed. This code gets called during device driver initialization
+// and after a reset from the host.
+static void
+ep0_init(void)
+{
+ // Reset the various fields in the ep0 structure.
+ ep0.common.buffer = (unsigned char*) 0;
+ ep0.common.buffer_size = 0;
+ ep0.common.fill_buffer_fn = (void (*)(usbs_control_endpoint*)) 0;
+ ep0.common.fill_data = (void*) 0;
+ ep0.common.fill_index = 0;
+ ep0.common.complete_fn = (usbs_control_return (*)(usbs_control_endpoint*, int)) 0;
+ ep0.rx_expecting_data = false;
+ ep0.tx_indicator_valid = false;
+ ep0.rx_indicator_valid = false;
+ ep0.tx_needs_zero_transfer = false;
+
+#ifdef CYGIMP_DEVS_USB_UPD985XX_SERIALIZE_TRANSMITS
+ tx_in_progress = false;
+ ep0_tx_pending = false;
+# ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+ ep3_tx_pending = false;
+# endif
+# ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+ ep5_tx_pending = false;
+# endif
+#endif
+
+ // The general mode register. We do not have an address yet. The
+ // SOFINTVL field needs to be set to its default value. The other
+ // bits should be zero for now.
+ *USBS_GMR = USBS_GMR_SOFINTVL_DEFAULT_VALUE; FLUSH_IBUS();
+
+ // The version register and the status registers are read-only.
+
+ // Interrupt masks. Endpoint 0 transmits and receives both have
+ // to be detected, as do the control operations. There should
+ // be no need to worry about full mailboxes or empty receive
+ // pools. DMA errors might be of interest, but it is not clear
+ // what to do about them since there does not appear to be
+ // a way of figuring out which transfer is affected. Frame number
+ // and addressing problems are ignored, there is nothing obvious
+ // that can be done about these. The other endpoints have their
+ // own initialization routines.
+ //
+ // Care has to be taken with reset interrupts. With some hardware
+ // the usb lines may be left floating during initialization, so
+ // the chip believes it sees continuous reset interrupts. There
+ // also appear to be problems if the host does generate a real
+ // reset signal, with interrupt storms lasting 10 or more
+ // milliseconds and preventing any other activity from taking
+ // place. What is done here is that reset interrupts are enabled
+ // if in the initial POWERED state. When a reset is detected,
+ // either a spurious one or a real reset from the host,
+ // handle_reset() will move the target to DEFAULT state, call
+ // ep0_init() again, and reset interrupts will be masked out.
+ // When a real control request is received from the host we
+ // know we have a good connection and the reset interrupt will
+ // be unmasked in ep0_rx_dsr(), so further resets from the
+ // host will be processed correctly. If the target is disconnected
+ // then we may again get a spurious reset interrupt, so we end
+ // up back in DEFAULT state and the reset interrupt would be
+ // masked again.
+ *USBS_IMR1 = IBUS_SWAP32(USBS_GSR1_GSR2 | USBS_GSR1_EP0TF | USBS_GSR1_EP0RF); FLUSH_IBUS();
+ usbs_upd985xx_gsr1_mask = (USBS_GSR1_EP0TF | USBS_GSR1_EP0RF);
+ if (USBS_STATE_DEFAULT == (ep0.common.state & USBS_STATE_MASK)) {
+ *USBS_IMR2 = IBUS_SWAP32(USBS_GSR2_URSM | USBS_GSR2_USPD); FLUSH_IBUS();
+ usbs_upd985xx_gsr2_mask = (USBS_GSR2_URSM | USBS_GSR2_USPD);
+ } else {
+ *USBS_IMR2 = IBUS_SWAP32(USBS_GSR2_URSM | USBS_GSR2_URST | USBS_GSR2_USPD); FLUSH_IBUS();
+ usbs_upd985xx_gsr2_mask = (USBS_GSR2_URSM | USBS_GSR2_URST | USBS_GSR2_USPD);
+ }
+
+ // Writing to the command register is a bad idea, because even
+ // writing 0 constitutes a command. Similarly there is no point
+ // in writing to the command address register.
+
+ // The endpoint status register is read-only.
+
+ // Set the rx pool information registers to disable alerts.
+ *USBS_RP0IR = IBUS_SWAP32(USBS_RPxIR_AL_NONE); FLUSH_IBUS();
+ *USBS_RP1IR = IBUS_SWAP32(USBS_RPxIR_AL_NONE); FLUSH_IBUS();
+ *USBS_RP2IR = IBUS_SWAP32(USBS_RPxIR_AL_NONE); FLUSH_IBUS();
+
+ // The pool address registers are read-only. The documentation
+ // that describes initialization says that these registers need to
+ // be filled in, but that seems wrong: providing receive buffers
+ // involves the command register. Presumably on early revisions it
+ // was necessary to fill in the address register.
+
+ // Sort out the mailboxes.
+ *USBS_TMSA = IBUS_SWAPPTR(TxMailbox, &(uncached->tx_mboxes[0])); FLUSH_IBUS();
+ *USBS_TMBA = IBUS_SWAPPTR(TxMailbox, &(uncached->tx_mboxes[TXMBOX_COUNT])); FLUSH_IBUS();
+ *USBS_RMSA = IBUS_SWAPPTR(RxMailbox, &(uncached->rx_mboxes[0])); FLUSH_IBUS();
+ *USBS_RMBA = IBUS_SWAPPTR(RxMailbox, &(uncached->rx_mboxes[RXMBOX_COUNT])); FLUSH_IBUS();
+ // It is not clear whether these registers actually need to be initialized.
+ // The documentation suggests that they do, unlike TMWA and RMWA which
+ // are taken care of by the hardware.
+#if 0
+ *USBS_TMRA = IBUS_SWAPPTR(TxMailbox, &(uncached->tx_mboxes[0])); FLUSH_IBUS();
+ *USBS_RMRA = IBUS_SWAPPTR(RxMailbox, &(uncached->rx_mboxes[0])); FLUSH_IBUS();
+#endif
+
+ // Start a receive operation for a control message.
+ ep0_start_rx(8);
+
+ // The endpoint 0 control register. The control packet size is
+ // configurable, with a default value of 8. Setting the
+ // enabled bit here affects the state as seen by the host.
+ *EP0_CR = IBUS_SWAP32(EP0_CR_EP0EN | CYGNUM_DEVS_USB_UPD985XX_EP0_PKTSIZE); FLUSH_IBUS();
+
+ // The other endpoint registers will be initialized by the appropriate
+ // _init() functions. Note that those other _init() functions should
+ // probably be called before the ep0-enabled bit is set.
+}
+
+// ----------------------------------------------------------------------------
+// Endpoint 1 - isochronous transmits.
+#if 0
+// A real implementation
+#else
+static inline void
+ep1_init(void)
+{
+ *EP1_CR = IBUS_SWAP32(0); // Clear EP1EN bit, thus disabling the endpoint
+ FLUSH_IBUS();
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// Endpoint 2 - isochronous receives
+#if 0
+// A real implementation
+#else
+static inline void
+ep2_init(void)
+{
+ *EP2_CR = IBUS_SWAP32(0); // Clear EP2EN bit, thus disabling the endpoint
+ FLUSH_IBUS();
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// Generic transmit support. This is intended for use with both endpoints
+// 3 and 5. For now the endpoint 0 code is too different.
+
+#if defined(CYGPKG_DEVS_USB_UPD985XX_EP3) || defined(CYGPKG_DEVS_USB_UPD985XX_EP5)
+
+// A utility routine for completing a transfer. This takes care of the
+// callback as well as resetting the buffer.
+static void
+ep35_tx_complete(ep35_impl* ep, int result)
+{
+ void (*complete_fn)(void*, int) = ep->common.complete_fn;
+ void* complete_data = ep->common.complete_data;
+
+ ep->common.buffer = (unsigned char*) 0;
+ ep->common.buffer_size = 0;
+ ep->common.complete_fn = (void (*)(void*, int)) 0;
+ ep->common.complete_data = (void*) 0;
+
+ if ((void (*)(void*, int))0 != complete_fn) {
+ (*complete_fn)(complete_data, result);
+ }
+}
+
+static void
+ep35_start_tx(ep35_impl* ep)
+{
+ // Is this endpoint currently stalled? If so then a size of 0 can
+ // be used to block until the stall condition is clear, anything
+ // else should result in an immediate callback.
+ if (ep->common.halted) {
+ if (0 != ep->common.buffer_size) {
+ ep35_tx_complete(ep, -EAGAIN);
+ }
+ } else if (0 == ep->common.buffer_size) {
+ // A check to see if the endpoint is halted. It isn't.
+ ep35_tx_complete(ep, 0);
+ } else {
+ cyg_uint32 send_command;
+#if 0
+ diag_printf("Tx: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep->common.buffer[0], ep->common.buffer[1], ep->common.buffer[2], ep->common.buffer[3],
+ ep->common.buffer[4], ep->common.buffer[5], ep->common.buffer[6], ep->common.buffer[7]);
+ diag_printf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep->common.buffer[8], ep->common.buffer[9], ep->common.buffer[10], ep->common.buffer[11],
+ ep->common.buffer[12], ep->common.buffer[13], ep->common.buffer[14], ep->common.buffer[15]);
+ diag_printf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep->common.buffer[16], ep->common.buffer[17], ep->common.buffer[18], ep->common.buffer[19],
+ ep->common.buffer[20], ep->common.buffer[21], ep->common.buffer[22], ep->common.buffer[23]);
+ diag_printf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep->common.buffer[24], ep->common.buffer[25], ep->common.buffer[26], ep->common.buffer[27],
+ ep->common.buffer[28], ep->common.buffer[29], ep->common.buffer[30], ep->common.buffer[31]);
+ diag_printf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep->common.buffer[32], ep->common.buffer[33], ep->common.buffer[34], ep->common.buffer[35],
+ ep->common.buffer[36], ep->common.buffer[37], ep->common.buffer[38], ep->common.buffer[39]);
+ diag_printf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep->common.buffer[40], ep->common.buffer[41], ep->common.buffer[42], ep->common.buffer[43],
+ ep->common.buffer[44], ep->common.buffer[45], ep->common.buffer[46], ep->common.buffer[47]);
+#endif
+
+ // Update the static buffer descriptor.
+ ep->tx_bufdesc->buffer = MIPS_TO_UNCACHED(ep->common.buffer);
+ ep->tx_bufdesc->control = ep->common.buffer_size | TXBUFDESC_CTRL_LAST | TXBUFDESC_CTRL_BUFDESC_BUFDESC;
+
+ // Make sure that the entire transmit buffer is flushed to
+ // memory.
+ HAL_DCACHE_STORE(ep->common.buffer, ep->common.buffer_size);
+
+ // Issue the send command. It is known that no transmits are
+ // in progress for this endpoint so the upper bound of 2
+ // pending transmits can be ignored. The send command involves
+ // writing to two registers in succession, so interrupts had
+ // better be disabled while doing this.
+ send_command = ep->send_command | ep->common.buffer_size;
+ cyg_drv_isr_lock();
+ while (0 != (*USBS_CMR & IBUS_SWAP32(USBS_CMR_BUSY))) {
+ // Do nothing: this situation should be short-lived.
+ }
+ *USBS_CA = IBUS_SWAPPTR(void, (void*)ep->tx_bufdesc); FLUSH_IBUS();
+ *USBS_CMR = IBUS_SWAP32(send_command); FLUSH_IBUS();
+ cyg_drv_isr_unlock();
+ }
+}
+
+// The stalled state is controlled by a single bit in the appropriate
+// control register. However there is a problem in that it is not
+// possible to abort a transmission that is already going out.
+// Furthermore there is no way of detecting whether or not any
+// packets have already gone out for this transfer: setting the halt
+// bit before any data has gone out is reasonably ok, doing so
+// in the middle of a transfer could be confusing.
+//
+// The approach taken here is to check whether or not there is a
+// current tx buffer. If not then the stall bit can be set immediately.
+// Otherwise the halted flag is set here, and it is left to the dsr
+// to set the stall bit when the transfer completes. This may not
+// be totally standards-compliant, but is probably the best solution
+// for now.
+static void
+ep35_set_halted(ep35_impl* ep, cyg_bool new_value)
+{
+ if (ep->common.halted == new_value) {
+ return;
+ }
+
+ // Avoid race conditions with the DSR updating the buffer fields.
+ cyg_drv_dsr_lock();
+
+ if (new_value ){
+ // Set the halted flag to prevent further transmits, and if
+ // there is no transmission currently in progress then set
+ // the stalled bit immediately.
+ ep->common.halted = true;
+ if ((void*)0 == ep->common.buffer) {
+ *(ep->cr) |= IBUS_SWAP32(EPtx_CR_SSx); FLUSH_IBUS();
+ }
+ } else {
+ // Update the hardware (that may be a no-op if the stalled bit
+ // never got set by the DSR), and clear the halted flag.
+ *(ep->cr) &= IBUS_SWAP32(~EPtx_CR_SSx); FLUSH_IBUS();
+ ep->common.halted = false;
+
+ // If there is a pending request to wait until the endpoint stall
+ // condition is clear, inform higher-level code. This test may
+ // give false positives but those would be harmless.
+ if (0 == ep->common.buffer_size) {
+ ep35_tx_complete(ep, 0);
+ }
+ }
+
+ cyg_drv_dsr_unlock();
+}
+
+// An interrupt has occured related to endpoint 3 or 5 - i.e. the EP3TF
+// or EP5TF interrupts, nothing else seems especially relevant. It is
+// necessary to extract the appropriate send indicator from the tx
+// mailbox to determine whether or not the transmission was successful
+// and report status to higher-level code.
+static void
+ep35_dsr(ep35_impl* ep)
+{
+ TxMailbox mbox;
+
+ // Extract the transmit indicator if that has not happened
+ // already courtesy of another DSR.
+ if (!ep->tx_indicator_valid) {
+ drain_tx_mailbox();
+ if (!ep->tx_indicator_valid) {
+ // A transmit interrupt when there does not appear to be
+ // any data?
+ CYG_FAIL("ep35_dsr invoked when there is no valid tx indicator");
+ return;
+ }
+ }
+ mbox = ep->tx_indicator;
+ ep->tx_indicator_valid = false;
+
+#ifdef CYGIMP_DEVS_USB_UPD985XX_EP5_BULK
+ // If emulating bulk transfers over the interrupt endpoint, and
+ // the transfer is an exact multiple of 64 bytes, then an extra
+ // zero-byte terminating packet needs to be sent. Care has to be
+ // taken to do this only once.
+ if ( (ep == &ep5) && (0 == (ep5.common.buffer_size % 64)) ) {
+ static cyg_bool sending_zero = false;
+ if (!sending_zero) {
+ sending_zero = true;
+ uncached->ep5_tx_bufdesc.buffer = uncached->ep0_tx_buffer;
+ uncached->ep5_tx_bufdesc.control = 0 | TXBUFDESC_CTRL_LAST | TXBUFDESC_CTRL_BUFDESC_BUFDESC;
+ cyg_drv_isr_lock();
+ while (0 != (*USBS_CMR & IBUS_SWAP32(USBS_CMR_BUSY))) {
+ // Do nothing: this situation should be short-lived.
+ }
+ *USBS_CA = IBUS_SWAPPTR(void, (void*) &(uncached->ep5_tx_bufdesc)); FLUSH_IBUS();
+ *USBS_CMR = IBUS_SWAP32(USBS_CMR_COMMAND_TX_EP5 | 0); FLUSH_IBUS();
+ cyg_drv_isr_unlock();
+
+ // Do not complete the transfer. Instead completion has to
+ // wait for another interrupt.
+ return;
+ } else {
+ // This interrupt was for the zero-byte packet, so drop through
+ sending_zero = false;
+ }
+ }
+#endif
+
+ // If the endpoint should be halted but there was a transmit
+ // in progress, update the hardware now.
+ if (ep->common.halted) {
+ *(ep->cr) |= IBUS_SWAP32(EPtx_CR_SSx); FLUSH_IBUS();
+ }
+
+ // Allow any blocked transmits to proceed.
+ TX_UNLOCK();
+
+ if (0 != (mbox.status & TXMBOX_STATUS_IBUS_ERROR)) {
+ // This appears to be the only type of error that can be
+ // detected. Possibly the transmit should be retried here
+ // rather than reported.
+ ep35_tx_complete(ep, -EPIPE);
+ } else {
+ ep35_tx_complete(ep, ep->common.buffer_size);
+ }
+}
+#endif // Endpoints 3 or 5
+
+// ----------------------------------------------------------------------------
+// Endpoint 3 - bulk transmits.
+
+# ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+static void
+ep3_start_tx(usbs_tx_endpoint* endpoint)
+{
+ CYG_ASSERT( endpoint == &ep3.common, "USB data transfer involves the wrong endpoint");
+ CYG_ASSERT( ep3.common.buffer_size < (64 * 1024), "Specified transfer size too large for current implementation");
+ if (TX_TRY_LOCK(&ep3_tx_pending)) {
+ ep35_start_tx(&ep3);
+ }
+}
+
+static void
+ep3_set_halted(usbs_tx_endpoint* endpoint, cyg_bool new_value)
+{
+ CYG_ASSERT(endpoint = &ep3.common, "USB set-stall operation involves the wrong endpoint");
+ ep35_set_halted(&ep3, new_value);
+}
+
+// Initialization. This gets called during the device driver
+// initialization and after a reset. The main job is to initialize the
+// EP3 control register, but the relevant bits of the interrupt mask
+// are set here as well. The tx mailboxes are shared with other
+// endpoints, so that is handled by ep0_init(). Any traffic that
+// happened before the reset needs to be cleaned up.
+static void
+ep3_init(void)
+{
+ // Assume 64 byte packets, terminate transfers with a zero-byte packet
+ // if necessary since this endpoint is used for bulk transfers.
+ *EP3_CR = IBUS_SWAP32(EP3_CR_EP3EN | EP3_CR_TM3_SZLP | 64); FLUSH_IBUS();
+ *USBS_IMR1 |= IBUS_SWAP32(USBS_GSR1_EP3TF); FLUSH_IBUS();
+ usbs_upd985xx_gsr1_mask |= IBUS_SWAP32(USBS_GSR1_EP3TF);
+ ep3.common.halted = false;
+ ep3.tx_indicator_valid = false;
+ ep3.tx_bufdesc = &(uncached->ep3_tx_bufdesc);
+ ep35_tx_complete(&ep3, -EPIPE);
+}
+#else
+static inline void
+ep3_init(void)
+{
+ *EP3_CR = 0; // Clear EP3EN bit, thus disabling the endpoint
+ FLUSH_IBUS();
+}
+# endif // Endpoint 3 configured in
+
+// ----------------------------------------------------------------------------
+// Repeat for endpoint 5
+# ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+
+static void
+ep5_start_tx(usbs_tx_endpoint* endpoint)
+{
+ CYG_ASSERT( endpoint == &ep5.common, "USB data transfer involves the wrong endpoint");
+#ifdef CYGIMP_DEVS_USB_UPD985XX_EP5_BULK
+ CYG_ASSERT( ep5.common.buffer_size < (64 * 1024), "Specified transfer size too large for current implementation");
+#else
+ CYG_ASSERT( ep5.common.buffer_size <= 64, "Specified transfer size too large for current implementation");
+#endif
+ if (TX_TRY_LOCK(&ep5_tx_pending)) {
+ ep35_start_tx(&ep5);
+ }
+}
+
+static void
+ep5_set_halted(usbs_tx_endpoint* endpoint, cyg_bool new_value)
+{
+ CYG_ASSERT(endpoint = &ep5.common, "USB set-stall operation involves the wrong endpoint");
+ ep35_set_halted(&ep5, new_value);
+}
+
+// Initialization. This gets called during the device driver
+// initialization and after a reset. The main job is to initialize the
+// EP5 control register, but the relevant bits of the interrupt mask
+// are set here as well. The tx mailboxes are shared with other
+// endpoints, so that is handled by ep0_init(). Any traffic that
+// happened before the reset needs to be cleaned up.
+static void
+ep5_init(void)
+{
+ // Assume 64 byte packets, terminate transfers with a zero-byte packet
+ // if necessary since this endpoint is used for bulk transfers.
+ *EP5_CR = IBUS_SWAP32(EP5_CR_EP5EN | 64); FLUSH_IBUS();
+ *USBS_IMR1 |= IBUS_SWAP32(USBS_GSR1_EP5TF); FLUSH_IBUS();
+ usbs_upd985xx_gsr1_mask |= IBUS_SWAP32(USBS_GSR1_EP5TF);
+ ep5.common.halted = false;
+ ep5.tx_indicator_valid = false;
+ ep5.tx_bufdesc = &(uncached->ep5_tx_bufdesc);
+ ep35_tx_complete(&ep5, -EPIPE);
+}
+#else
+static inline void
+ep5_init(void)
+{
+ *EP5_CR = 0; // Clear EP5EN bit, thus disabling the endpoint
+ FLUSH_IBUS();
+}
+#endif // Endpoint 5 configured in
+
+
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
+// ----------------------------------------------------------------------------
+// Endpoint 4 - bulk receives.
+//
+// Bulk receives are mostly straightforward, but the cache does involve
+// a complication. The assumption is that the receive buffer will be in
+// cached memory, and is unlikely to be cacheline-aligned. Clearly the bulk
+// of the buffer has to be invalidated. However the receive buffer may share
+// some cache lines with other data at the head and tail, and invalidating
+// those would be wrong.
+//
+// The solution here is to split up a receive in to up to three areas,
+// head, main, and tail, where the head and tail are statically
+// allocated in uncached memory. Any one or two of these areas may be
+// unused, depending on alignment and transfer size. The main area
+// corresponds to the central section of the supplied receive buffer,
+// will be cacheline-aligned, and invalidated at the start of a receive.
+// Data will be copied from the head and tail areas into the receive
+// buffer by the dsr on completion of the transfer.
+//
+// There are additional complications caused by the hardware's need for
+// linked buffers.
+
+static void
+ep4_rx_complete(int result)
+{
+ void (*complete_fn)(void*, int) = ep4.common.complete_fn;
+ void* complete_data = ep4.common.complete_data;
+
+#if 0
+ *EP4_CR = IBUS_SWAP32(EP4_CR_EP4EN | EP4_CR_RM4_ASSEMBLE | EP4_CR_NAK4 | 64); FLUSH_IBUS();
+#endif
+
+#if 0
+ if (result > 0) {
+ diag_printf("Rx: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep4.common.buffer[0], ep4.common.buffer[1], ep4.common.buffer[2], ep4.common.buffer[3],
+ ep4.common.buffer[4], ep4.common.buffer[5], ep4.common.buffer[6], ep4.common.buffer[7]);
+ diag_printf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep4.common.buffer[8], ep4.common.buffer[9], ep4.common.buffer[10], ep4.common.buffer[11],
+ ep4.common.buffer[12], ep4.common.buffer[13], ep4.common.buffer[14], ep4.common.buffer[15]);
+ diag_printf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep4.common.buffer[16], ep4.common.buffer[17], ep4.common.buffer[18], ep4.common.buffer[19],
+ ep4.common.buffer[20], ep4.common.buffer[21], ep4.common.buffer[22], ep4.common.buffer[23]);
+ diag_printf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep4.common.buffer[24], ep4.common.buffer[25], ep4.common.buffer[26], ep4.common.buffer[27],
+ ep4.common.buffer[28], ep4.common.buffer[29], ep4.common.buffer[30], ep4.common.buffer[31]);
+ diag_printf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep4.common.buffer[32], ep4.common.buffer[33], ep4.common.buffer[34], ep4.common.buffer[35],
+ ep4.common.buffer[36], ep4.common.buffer[37], ep4.common.buffer[38], ep4.common.buffer[39]);
+ diag_printf(" %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ep4.common.buffer[40], ep4.common.buffer[41], ep4.common.buffer[42], ep4.common.buffer[43],
+ ep4.common.buffer[44], ep4.common.buffer[45], ep4.common.buffer[46], ep4.common.buffer[47]);
+ }
+#endif
+
+ ep4.common.buffer = (unsigned char*) 0;
+ ep4.common.buffer_size = 0;
+ ep4.common.complete_fn = (void (*)(void*, int)) 0;
+ ep4.common.complete_data = (void*) 0;
+
+ if ((void (*)(void*, int))0 != complete_fn) {
+ (*complete_fn)(complete_data, result);
+ }
+}
+
+static void
+ep4_start_rx(usbs_rx_endpoint* ep)
+{
+ CYG_ASSERT( ep == &ep4.common, "USB data transfer involves the wrong endpoint");
+
+ // Is this endpoint currently stalled? If so then a size of 0 can
+ // be used to block until the stall condition is clear, anything
+ // else should result in an immediate callback.
+ if (ep4.common.halted) {
+ if (0 != ep4.common.buffer_size) {
+ ep4_rx_complete(-EAGAIN);
+ }
+ } else if (0 == ep4.common.buffer_size) {
+ // A check to see if the endpoint is halted. It isn't.
+ ep4_rx_complete(0);
+ } else {
+
+ // Time to work out how much data should go into the uncached
+ // head and tail buffers, how much can go directly into the
+ // receive buffer, how much memory needs to be invalidated, and so on.
+ cyg_uint32 buffer_arith;
+
+ // Where to start filling in buffer descriptors.
+ cyg_uint32 first_bufdesc;
+
+ // And the current buffer descriptor.
+ cyg_uint32 current_bufdesc;
+
+ CYG_ASSERT( ep4.common.buffer_size < (64 * 1024), "Specified transfer size too large for current implementation");
+
+ // If there has not been a receive operation, tail_index
+ // will still be set to -1. Otherwise it will be somewhere
+ // between 1 and 3, or between 5 and 7, depending on
+ // whether the previous receive operation used the
+ // first four buffer descriptors or the last four.
+ if (ep4.tail_index < 4) {
+ first_bufdesc = 4;
+ } else {
+ first_bufdesc = 0;
+ }
+ current_bufdesc = first_bufdesc;
+
+ // Arithmetic, especially remainder operators, requires
+ // integers rather than a pointer.
+ buffer_arith = (cyg_uint32) ep4.common.buffer;
+
+ // The size of the "head" area. This involves up to
+ // (cacheline-1) bytes, so that the main receive buffer
+ // is suitably aligned.
+ ep4.head_size = ((buffer_arith + HAL_DCACHE_LINE_SIZE - 1) & ~(HAL_DCACHE_LINE_SIZE - 1)) - buffer_arith;
+ if (ep4.head_size > ep4.common.buffer_size) {
+ ep4.head_size = ep4.common.buffer_size;
+ }
+ if (0 < ep4.head_size) {
+ // It is necessary to receive some data into the uncached head area.
+ uncached->ep4_rx_bufdescs[current_bufdesc].buffer = uncached->ep4_head;
+ uncached->ep4_rx_bufdescs[current_bufdesc].control = RXBUFDESC_CTRL_BUFDESC_BUFDESC | ep4.head_size;
+ current_bufdesc++;
+ }
+
+ // Now for the size of the main area. This is the rest of the
+ // transfer size, minus the tail area.
+ ep4.direct_size = ep4.common.buffer_size - ep4.head_size;
+ ep4.direct_size &= ~(HAL_DCACHE_LINE_SIZE - 1);
+ if (ep4.direct_size > 0) {
+ uncached->ep4_rx_bufdescs[current_bufdesc].buffer = MIPS_TO_UNCACHED(ep4.common.buffer + ep4.head_size);
+ uncached->ep4_rx_bufdescs[current_bufdesc].control = RXBUFDESC_CTRL_BUFDESC_BUFDESC | ep4.direct_size;
+ current_bufdesc++;
+ HAL_DCACHE_INVALIDATE(ep4.common.buffer + ep4.head_size, ep4.direct_size);
+ }
+
+ // And the size of the tail. This is the transfer size minus what we have accumulated so far.
+ ep4.tail_size = ep4.common.buffer_size - (ep4.head_size + ep4.direct_size);
+ if (ep4.tail_size > 0) {
+ uncached->ep4_rx_bufdescs[current_bufdesc].buffer = uncached->ep4_tail;
+ uncached->ep4_rx_bufdescs[current_bufdesc].control = RXBUFDESC_CTRL_BUFDESC_BUFDESC | ep4.tail_size;
+ current_bufdesc++;
+ }
+
+ // Or the LAST bit into the last of these buffer descriptors.
+ uncached->ep4_rx_bufdescs[current_bufdesc - 1].control |= RXBUFDESC_CTRL_LAST;
+
+ // Turn the current one into a link descriptor.
+ uncached->ep4_rx_bufdescs[current_bufdesc].control = RXBUFDESC_CTRL_BUFDESC_LINK;
+ uncached->ep4_rx_bufdescs[current_bufdesc].buffer = 0;
+
+ // The buffer descriptors have now been sorted out. Time to
+ // add this receive buffer to the pool. Atomicity becomes
+ // important for some of these steps.
+ cyg_drv_isr_lock();
+
+ // Update the link pointer used for the last receive operation
+ // to point at the new set of buffer descriptors.
+ if (-1 != ep4.tail_index) {
+ uncached->ep4_rx_bufdescs[ep4.tail_index].buffer = (void*) &(uncached->ep4_rx_bufdescs[first_bufdesc]);
+ }
+
+ // Keep track of the link pointer used for the last receive
+ // operation.
+ ep4.tail_index = current_bufdesc;
+
+ while (0 != (*USBS_CMR & IBUS_SWAP32(USBS_CMR_BUSY))) {
+ // Do nothing: this situation should be short-lived.
+ }
+ *USBS_CA = IBUS_SWAPPTR(void, (void*)&(uncached->ep4_rx_bufdescs[first_bufdesc])); FLUSH_IBUS();
+ *USBS_CMR = IBUS_SWAP32(USBS_CMR_COMMAND_ADD_POOL2 | 1); FLUSH_IBUS();
+#if 0
+ *EP4_CR = IBUS_SWAP32(EP4_CR_EP4EN | EP4_CR_RM4_ASSEMBLE | 64); FLUSH_IBUS();
+#endif
+ cyg_drv_isr_unlock();
+ }
+}
+
+// The stalled state is controlled by a single bit in the appropriate
+// control register. When it comes to ongoing receives, the reasoning
+// here is much the same as for ep3_set_halted(). Arguably this is not
+// quite right. set_halted() is most likely to be called in response
+// to a control request from the host, and the host is unlikely to
+// transmit any data at the same time as making this request. Hence the
+// host will see the wrong behaviour: it can still make one transfer
+// after asking for the stalled bit to be set. Since there does not
+// appear to be any way to cancel a supplied receive, this behaviour
+// still seems the most sensible.
+static void
+ep4_set_halted(usbs_rx_endpoint* ep, cyg_bool new_state)
+{
+ CYG_ASSERT(ep == &ep4.common, "USB set-stall operation involves the wrong endpoint");
+
+ if (ep4.common.halted == new_state) {
+ return;
+ }
+
+ // Avoid race conditions with the DSR updating the buffer fields.
+ cyg_drv_dsr_lock();
+
+ if (new_state){
+ // Set the halted flag to prevent further transmits, and if
+ // there is no receive currently in progress then set
+ // the stalled bit immediately.
+ ep4.common.halted = true;
+ if ((void*)0 == ep4.common.buffer) {
+ *EP4_CR |= IBUS_SWAP32(EP4_CR_SS4); FLUSH_IBUS();
+ }
+ } else {
+ // Update the hardware (that may be a no-op if the stalled bit
+ // never got set by the DSR), and clear the halted flag.
+ *EP4_CR &= IBUS_SWAP32(~EP4_CR_SS4); FLUSH_IBUS();
+ ep4.common.halted = false;
+
+ // If there is a pending request to wait until the endpoint stall
+ // condition is clear, inform higher-level code. This test may
+ // give false positives but those would be harmless.
+ if (0 == ep4.common.buffer_size) {
+ ep4_rx_complete(0);
+ }
+ }
+
+ cyg_drv_dsr_unlock();
+}
+
+// An interrupt has occured related to endpoint 4 - i.e. the EP4RF
+// interrupt, nothing else seems especially relevant. The ISR will
+// have set the NAK bit in the control register. It is necessary to
+// extract the appropriate receive indicator from the rx mailbox to
+// determine whether or not the transmission was successful and how
+// much data was actually received, clear the interrupt bit, and
+// report status to higher-level code.
+static void
+ep4_dsr(void)
+{
+ // Extract the transmit indicator if that has not happened
+ // already courtesy of another DSR.
+ if (!ep4.rx_indicator_valid) {
+ drain_rx_mailbox();
+ if (!ep4.rx_indicator_valid) {
+ // A receive interrupt when there does not appear to be
+ // any data? It appears that this can happen when there
+ // are error conditions.
+#if 1
+ CYG_FAIL("EP4_DSR invoked when there is no valid rx indicator");
+#endif
+ return;
+ }
+ }
+ ep4.rx_indicator_valid = false;
+
+ // If the endpoint should be halted but there was a transmit
+ // in progress, update the hardware now.
+ if (ep4.common.halted) {
+ *EP4_CR |= IBUS_SWAP32(EP4_CR_SS4); FLUSH_IBUS();
+ }
+ if (0 != (ep4.rx_indicator.status & (RXMBOX_STATUS_IBUS_ERROR | 0))) {
+ // This appears to be the only type of error that can be
+ // detected. Everything else gets retried by the hardware,
+ // except when using the isochronous endpoint.
+ ep4_rx_complete(-EPIPE);
+ } else {
+ cyg_uint32 actual_size = ep4.rx_indicator.status & RXMBOX_STATUS_SIZE_MASK;
+
+ // Either the transfer size must match the requested size, or the
+ // supplied buffer should have been aligned to cacheline boundaries.
+ // Anything else risks leaving the receive pool in a confused
+ // state and there is no way of cleaning things up.
+ CYG_ASSERT((actual_size == ep4.common.buffer_size) || ((0 == ep4.head_size) && (0 == ep4.tail_size)), \
+ "Buffers should be cacheline aligned if the protocol involves partial transfers");
+
+ // If there was some data in the head, move it from uncached
+ // to cached. Ditto for tail. Note that these copies may be
+ // for data that has not actually been transferred if the
+ // actual transfer is less than expected, but overwriting bits
+ // of the receive buffer in such circumstances should be
+ // harmless.
+ if (ep4.head_size > 0) {
+ memcpy(ep4.common.buffer, uncached->ep4_head, ep4.head_size);
+ }
+ if (ep4.tail_size > 0) {
+ memcpy(ep4.common.buffer + ep4.head_size + ep4.direct_size, uncached->ep4_tail, ep4.tail_size);
+ }
+ ep4_rx_complete(actual_size);
+ }
+}
+
+// Initialization. This gets called during the device driver
+// initialization and after a reset. The main job is to initialize the
+// EP4 control register, but the relevant bits of the interrupt mask
+// are set here as well. The rx mailboxes are shared with other
+// endpoints, so that is handled by ep0_init(). Any traffic that
+// happened before the reset needs to be cleaned up.
+static void
+ep4_init(void)
+{
+ // Assume 64 byte packets, and use assemble mode so that we get a
+ // single rx indication per transfer. In practice the buffer
+ // directory will only ever contain one entry so there should be
+ // no discernible difference between normal, assemble, or separate
+ // mode.
+#if 0
+ *EP4_CR = IBUS_SWAP32(EP4_CR_EP4EN | EP4_CR_RM4_ASSEMBLE | EP4_CR_NAK4 | 64); FLUSH_IBUS();
+#else
+ *EP4_CR = IBUS_SWAP32(EP4_CR_EP4EN | EP4_CR_RM4_ASSEMBLE | 64); FLUSH_IBUS();
+#endif
+
+ *USBS_IMR1 |= IBUS_SWAP32(USBS_GSR1_EP4RF); FLUSH_IBUS();
+ usbs_upd985xx_gsr1_mask |= USBS_GSR1_EP4RF;
+ ep4.common.halted = false;
+ ep4.rx_indicator_valid = false;
+ ep4.tail_index = -1;
+ ep4_rx_complete(-EPIPE);
+}
+#else
+static inline void
+ep4_init(void)
+{
+ *EP4_CR = 0; // Clear EP4EN bit, thus disabling the endpoint
+ FLUSH_IBUS();
+}
+#endif // Endpoint 4 configured in
+
+// ----------------------------------------------------------------------------
+// Endpoint 6 - interrupt receives
+#if 0
+// A real implementation
+#else
+static inline void
+ep6_init(void)
+{
+ *EP6_CR = 0; // Clear EP6EN bit, thus disabling the endpoint
+ FLUSH_IBUS();
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// Make sure the hardware is in a known state by completely resetting
+// the USB controller. This gets called during device driver
+// initialization, and again whenever the host issues a reset signal.
+// The previous state is unknown. Even during eCos initialization
+// RedBoot may have involved USB I/O, or some POST code may have
+// performed loopback tests. The various endpoint init routines will
+// also perform software resets as appropriate.
+
+static void
+usbs_upd985xx_handle_reset(void)
+{
+ // Reset the USB hardware. This involves poking the warm reset
+ // register and then polling the matching status register. It is
+ // assumed that this poll will take a short time, and in practice
+ // the loop appears to terminate immediately.
+ *S_WRCR = S_WRCR_USBWR; FLUSH_IBUS();
+ while (0 == (*S_WRSR & S_WRCR_USBWR)) {
+ // Do nothing.
+ }
+
+ // Get all the endpoints into a known state - for disabled
+ // endpoints these init calls are inlined and just disable the
+ // relevant hardware.
+ ep0_init();
+ ep1_init();
+ ep2_init();
+ ep3_init();
+ ep4_init();
+ ep5_init();
+ ep6_init();
+}
+
+// ----------------------------------------------------------------------------
+// Start(). This is typically called by the application itself once
+// everything else has been initialized, i.e. when the host should be
+// able to start talking to this device. There is no actual bit in the
+// chip itself to switch the USB pins from tri-state, so instead the
+// platform HAL has to supply appropriate functionality. In the absence
+// of such functionality things will only work if you start up eCos
+// with the USB cable disconnected, then plug in the cable once
+// start() has been called.
+//
+// The device driver initialization will have already set up the
+// hardware, and the first action from the host should be a reset
+// signal which will cause a re-initialization. There is no need
+// to do anything else here.
+
+static void
+usbs_upd985xx_ep0_start(usbs_control_endpoint* endpoint)
+{
+ CYG_ASSERT( endpoint == &ep0.common, "USB startup involves the wrong endpoint");
+
+ // If there is additional platform-specific initialization to
+ // perform, do it now. This macro can come from the platform HAL,
+ // but may not be available on all platforms.
+#ifdef UPD985XX_USB_PLATFORM_INIT
+ UPD985XX_USB_PLATFORM_INIT();
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// The main DSR
+//
+// This gets called by the interrupt system or by the polling code
+// when one or more USB-related events have occurred. The ISR code
+// will have updated globals usbs_upd985xx_gsr1 and usbs_upd98x0x_gsr2
+// to indicate which events are pending. When running in interrupt
+// mode it is possible that further interrupts will occur while the
+// DSR is running, and hence that these globals will be updated
+// while the DSR is running. This is handled by a loop. A side effect
+// is that the DSR may get called when all the work has already
+// been done by a previous DSR, but that is harmless.
+
+static void
+usbs_upd985xx_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+ CYG_ASSERT(CYGNUM_HAL_INTERRUPT_USB == vector, "USB DSR should only be invoked for USB interrupts" );
+ CYG_ASSERT(0 == data, "The USB DSR needs no global data pointer");
+
+ while ((0 != usbs_upd985xx_gsr1) || (0 != usbs_upd985xx_gsr2)) {
+ // Only update the globals in one place since it involves
+ // the overhead of two function calls.
+ cyg_uint32 gsr1, gsr2;
+ cyg_drv_isr_lock();
+ gsr1 = usbs_upd985xx_gsr1;
+ gsr2 = usbs_upd985xx_gsr2;
+ usbs_upd985xx_gsr1 = 0;
+ usbs_upd985xx_gsr2 = 0;
+ cyg_drv_isr_unlock();
+
+ if (0 != gsr2) {
+ // Treat reset specially. If there has been a reset then none of
+ // the other bits are of any interest.
+ if (0 != (USBS_GSR2_URST & gsr2)) {
+ int old_state = ep0.common.state;
+ // Update the state. ep0_init() detects this state change and
+ // updates imr2 appropriate, preventing a continuous storm
+ // of reset interrupts.
+ ep0.common.state = USBS_STATE_DEFAULT;
+ usbs_upd985xx_handle_reset();
+ // This state change must be reported to higher-level code
+ if ((void (*)(usbs_control_endpoint*, void*, usbs_state_change, int))0 != ep0.common.state_change_fn) {
+ (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+ USBS_STATE_CHANGE_RESET, old_state);
+ }
+ break;
+ }
+ // There is possible confusion if both suspend and resume
+ // bits are set. Was there a suspend, quickly followed by
+ // a resume? Were we already suspended, then resumed, now
+ // suspended again? For now this complication is ignored and
+ // resume is given priority over suspend.
+ if (0 != (USBS_GSR2_URSM & gsr2)) {
+ int old_state = ep0.common.state;
+ if (0 != (old_state & USBS_STATE_SUSPENDED)) {
+ ep0.common.state &= ~USBS_STATE_SUSPENDED;
+ if ((void (*)(usbs_control_endpoint*, void*, usbs_state_change, int))0 != ep0.common.state_change_fn) {
+ (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+ USBS_STATE_CHANGE_RESUMED, old_state);
+ }
+ }
+ } else if (0 != (USBS_GSR2_USPD & gsr2)) {
+ int old_state = ep0.common.state;
+ if (0 == (old_state & USBS_STATE_SUSPENDED)) {
+ ep0.common.state |= USBS_STATE_SUSPENDED;
+ if ((void (*)(usbs_control_endpoint*, void*, usbs_state_change, int))0 != ep0.common.state_change_fn) {
+ (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+ USBS_STATE_CHANGE_SUSPENDED, old_state);
+ }
+ }
+ } else {
+ // Handle error conditions on the isochronous endpoints?
+ }
+ }
+ if (0 != gsr1) {
+
+ if (0 != (USBS_GSR1_EP0TF & gsr1)) {
+ ep0_tx_dsr();
+ }
+ if (0 != (USBS_GSR1_EP0RF & gsr1)) {
+ ep0_rx_dsr();
+ }
+#if 0
+ // EP1FU?
+ if (0 != (USBS_GSR1_EP1TF & gsr1)) {
+ ep1_dsr();
+ }
+#endif
+#if 0
+ // EP2FO?
+ if (0 != (USBS_GSR1_EP2RF & gsr1)) {
+ ep1_dsr();
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+ if (0 != (USBS_GSR1_EP3TF & gsr1)) {
+ ep35_dsr(&ep3);
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
+ if (0 != (USBS_GSR1_EP4RF & gsr1)) {
+ ep4_dsr();
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+ if (0 != (USBS_GSR1_EP5TF & gsr1)) {
+ ep35_dsr(&ep5);
+ }
+#endif
+#if 0
+ if (0 != (USBS_GSR1_EP6RF & gsr1)) {
+ ep6_dsr();
+ }
+#endif
+ }
+ } // while there are unprocessed interrupts
+}
+
+// ----------------------------------------------------------------------------
+// Interrupt handling.
+//
+// There are two status registers to look at. These are read-once
+// registers, i.e. reading the register causes all bits to be cleared,
+// so the relevant state has to be preserved in volatile globals which
+// can then be examined by the DSR. In theory the top bit of the first
+// status register can be used to check whether or not there is
+// anything of interest in the second one, but it seems quicker to
+// just read both registers. After masking out interrupts that are of
+// no interest, some global flags are updated. If this leaves a
+// non-zero value then the DSR must be invoked.
+
+static cyg_uint32
+usbs_upd985xx_isr(cyg_vector_t vector, cyg_addrword_t data)
+{
+ CYG_ASSERT(CYGNUM_HAL_INTERRUPT_USB == vector, "USB ISR should only be invoked for USB interrupts");
+ CYG_ASSERT(0 == data, "The UPD985xx ISR needs no global data pointer");
+
+ usbs_upd985xx_gsr1 |= IBUS_SWAP32(*USBS_GSR1) & usbs_upd985xx_gsr1_mask;
+ usbs_upd985xx_gsr2 |= IBUS_SWAP32(*USBS_GSR2) & usbs_upd985xx_gsr2_mask;
+
+ cyg_drv_interrupt_acknowledge(vector);
+ return ((0 == usbs_upd985xx_gsr1) && (0 == usbs_upd985xx_gsr2)) ?
+ CYG_ISR_HANDLED : CYG_ISR_CALL_DSR;
+}
+
+// ----------------------------------------------------------------------------
+// Polling support. It is not clear that this is going to work particularly
+// well since according to the documentation the hardware does not generate
+// NAKs automatically - instead the ISR has to set the appropriate bits
+// sufficiently quickly to avoid confusing the host.
+//
+// Calling the isr directly avoids duplicating code, but means that
+// cyg_drv_interrupt_acknowledge() will get called when not inside a
+// real interrupt handler. This should be harmless.
+
+static void
+usbs_upd985xx_poll(usbs_control_endpoint* endpoint)
+{
+ CYG_ASSERT(endpoint == &ep0.common, "USB poll involves the wrong endpoint");
+ if (CYG_ISR_CALL_DSR == usbs_upd985xx_isr(CYGNUM_HAL_INTERRUPT_USB, 0)) {
+ usbs_upd985xx_dsr(CYGNUM_HAL_INTERRUPT_USB, 0, 0);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Initialization
+//
+// This routine gets called from a prioritized static constructor during
+// eCos startup.
+void
+usbs_upd985xx_init(void)
+{
+ // Make sure the uncached data structure is accessed through
+ // kseg1.
+ uncached = (uncached_data*) MIPS_TO_UNCACHED(&cached_copy);
+
+ // Perform a full hardware reset.
+ usbs_upd985xx_handle_reset();
+
+ // It is possible and desirable to install the interrupt handler
+ // here, even though there will be no interrupts for a while yet.
+ // FIXME: is 99 a sensible interrupt priority :-?
+ cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_USB,
+ 99, // priority
+ 0, // data
+ &usbs_upd985xx_isr,
+ &usbs_upd985xx_dsr,
+ &usbs_upd985xx_intr_handle,
+ &usbs_upd985xx_intr_data);
+
+ cyg_drv_interrupt_attach(usbs_upd985xx_intr_handle);
+ cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_USB);
+}
+
+// ----------------------------------------------------------------------------
+// Testing support.
+
+usbs_testing_endpoint usbs_testing_endpoints[] = {
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL,
+ endpoint_number : 0,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &ep0.common,
+#ifdef CYGVAR_DEVS_USB_UPD985XX_EP0_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME "0c",
+#else
+ devtab_entry : (const char*) 0,
+#endif
+ min_size : 1, // zero-byte control transfers are meaningless
+#if (CYGNUM_DEVS_USB_UPD985XX_EP0_RXBUFSIZE < CYGNUM_DEVS_USB_UPD985XX_EP0_TXBUFSIZE)
+ max_size : CYGNUM_DEVS_USB_UPD985XX_EP0_RXBUFSIZE,
+#else
+ max_size : CYGNUM_DEVS_USB_UPD985XX_EP0_TXBUFSIZE,
+#endif
+ max_in_padding : 0,
+ alignment : 0
+ },
+
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+ endpoint_number : 3,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &ep3.common,
+# ifdef CYGVAR_DEVS_USB_UPD985XX_EP3_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME "3w",
+# else
+ devtab_entry : (const char*) 0,
+# endif
+ min_size : 0,
+ max_size : 0x0FFFF, // Driver limitation, only a single buffer descriptor is used
+ max_in_padding : 0,
+ alignment : HAL_DCACHE_LINE_SIZE
+ },
+#endif
+
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+ endpoint_number : 4,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT,
+ endpoint : (void*) &ep4.common,
+# ifdef CYGVAR_DEVS_USB_UPD985XX_EP4_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME "4r",
+# else
+ devtab_entry : (const char*) 0,
+# endif
+ min_size : 1,
+ max_size : 0x0FFFF, // Driver limitation
+ max_in_padding : 0,
+ alignment : HAL_DCACHE_LINE_SIZE
+ },
+#endif
+
+#ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
+ {
+# ifdef CYGIMP_DEVS_USB_UPD985XX_EP5_BULK
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+# else
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_INTERRUPT,
+# endif
+ endpoint_number : 5,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &ep5.common,
+# ifdef CYGVAR_DEVS_USB_UPD985XX_EP5_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME "5w",
+# else
+ devtab_entry : (const char*) 0,
+# endif
+ min_size : 1,
+ max_size : 0x0FFFF, // Driver limitation, only a single buffer descriptor is used
+ max_in_padding : 0,
+ alignment : HAL_DCACHE_LINE_SIZE
+ },
+#endif
+
+ USBS_TESTING_ENDPOINTS_TERMINATOR
+};
diff --git a/ecos/packages/devs/usb/nec_upd985xx/current/src/usbs_upd985xx_data.cxx b/ecos/packages/devs/usb/nec_upd985xx/current/src/usbs_upd985xx_data.cxx
new file mode 100644
index 0000000..45acda8
--- /dev/null
+++ b/ecos/packages/devs/usb/nec_upd985xx/current/src/usbs_upd985xx_data.cxx
@@ -0,0 +1,189 @@
+//==========================================================================
+//
+// usbs_nec_upd9850x.c
+//
+// Static data for the NEC uPD9850x USB device driver
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 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): bartv
+// Contributors: bartv
+// Date: 2001-05-22
+//
+// This file contains various objects that should go into extras.o
+// rather than libtarget.a, e.g. devtab entries that would normally
+// be eliminated by the selective linking.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/infra/diag.h>
+#include <cyg/io/devtab.h>
+#include <cyg/io/usb/usbs_upd985xx.h>
+#include <pkgconf/devs_usb_upd985xx.h>
+
+// ----------------------------------------------------------------------------
+// Initialization. The goal here is to call usbs_upd985xx_init()
+// early on during system startup, to take care of things like
+// registering interrupt handlers etc. which are best done
+// during system init.
+//
+// If the endpoint 0 devtab entry is available then its init()
+// function can be used to take care of this. However the devtab
+// entries are optional so an alternative mechanism must be
+// provided. Unfortunately although it is possible to give
+// a C function the constructor attribute, it cannot be given
+// an initpri attribute. Instead it is necessary to define a
+// dummy C++ class.
+
+extern "C" void usbs_upd985xx_init(void);
+
+#ifndef CYGVAR_DEVS_USB_UPD985XX_EP0_DEVTAB_ENTRY
+class usbs_upd985xx_initialization {
+ public:
+ usbs_upd985xx_initialization() {
+ usbs_upd985xx_init();
+ }
+};
+
+static usbs_upd985xx_initialization usbs_upd985xx_init_object CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_IO);
+#endif
+
+// ----------------------------------------------------------------------------
+// The devtab entries. Each of these is optional, many applications
+// will want to use the lower-level API rather than go via
+// open/read/write/ioctl.
+
+#ifdef CYGVAR_DEVS_USB_UPD985XX_EP0_DEVTAB_ENTRY
+
+// For endpoint 0 the only legal operations are get_config() and
+// set_config(), and these are provided by the common package.
+
+static bool
+usbs_upd985xx_devtab_ep0_init(struct cyg_devtab_entry* tab)
+{
+ CYG_UNUSED_PARAM(struct cyg_devtab_entry*, tab);
+ usbs_upd985xx_init();
+ return true;
+}
+
+static CHAR_DEVIO_TABLE(usbs_upd985xx_ep0_devtab_functions,
+ &cyg_devio_cwrite,
+ &cyg_devio_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+static CHAR_DEVTAB_ENTRY(usbs_upd985xx_ep0_devtab_entry,
+ CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME "0c",
+ 0,
+ &usbs_upd985xx_ep0_devtab_functions,
+ &usbs_upd985xx_devtab_ep0_init,
+ 0,
+ (void*) &usbs_upd985xx_ep0);
+#endif
+
+// ----------------------------------------------------------------------------
+// Common routines for ep3, ep4 and ep5
+#if defined(CYGVAR_DEVS_USB_UPD985XX_EP3_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_UPD985XX_EP4_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_UPD985XX_EP5_DEVTAB_ENTRY)
+static bool
+usbs_upd985xx_devtab_dummy_init(struct cyg_devtab_entry* tab)
+{
+ CYG_UNUSED_PARAM(struct cyg_devtab_entry*, tab);
+ return true;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// ep3 devtab entry. This can only be used for slave->host, so only
+// the cwrite() function makes sense. The same function table can be
+// used for ep5.
+
+#if defined(CYGVAR_DEVS_USB_UPD985XX_EP3_DEVTAB_ENTRY) || \
+ defined(CYGVAR_DEVS_USB_UPD985XX_EP5_DEVTAB_ENTRY)
+static CHAR_DEVIO_TABLE(usbs_upd985xx_ep35_devtab_functions,
+ &usbs_devtab_cwrite,
+ &cyg_devio_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+# if defined(CYGVAR_DEVS_USB_UPD985XX_EP3_DEVTAB_ENTRY)
+static CHAR_DEVTAB_ENTRY(usbs_upd985xx_ep3_devtab_entry,
+ CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME "3w",
+ 0,
+ &usbs_upd985xx_ep35_devtab_functions,
+ &usbs_upd985xx_devtab_dummy_init,
+ 0,
+ (void*) &usbs_upd985xx_ep3);
+
+# endif
+
+# if defined(CYGVAR_DEVS_USB_UPD985XX_EP5_DEVTAB_ENTRY)
+static CHAR_DEVTAB_ENTRY(usbs_upd985xx_ep5_devtab_entry,
+ CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME "5w",
+ 0,
+ &usbs_upd985xx_ep35_devtab_functions,
+ &usbs_upd985xx_devtab_dummy_init,
+ 0,
+ (void*) &usbs_upd985xx_ep5);
+# endif
+#endif
+
+// ----------------------------------------------------------------------------
+// ep4 devtab entry. This can only be used for host->slave, so only the
+// cread() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_UPD985XX_EP4_DEVTAB_ENTRY
+
+static CHAR_DEVIO_TABLE(usbs_upd985xx_ep4_devtab_functions,
+ &cyg_devio_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+static CHAR_DEVTAB_ENTRY(usbs_upd985xx_ep4_devtab_entry,
+ CYGDAT_DEVS_USB_UPD985XX_DEVTAB_BASENAME "4r",
+ 0,
+ &usbs_upd985xx_ep4_devtab_functions,
+ &usbs_upd985xx_devtab_dummy_init,
+ 0,
+ (void*) &usbs_upd985xx_ep4);
+#endif
+
diff --git a/ecos/packages/devs/usb/sa11x0/current/ChangeLog b/ecos/packages/devs/usb/sa11x0/current/ChangeLog
new file mode 100644
index 0000000..1571bbe
--- /dev/null
+++ b/ecos/packages/devs/usb/sa11x0/current/ChangeLog
@@ -0,0 +1,185 @@
+2003-02-25 Jonathan Larmour <jifl@eCosCentric.com>
+
+ * doc/usbs_sa11x0.sgml: Declare as <part> not <reference> to get
+ correct TOC numbering.
+
+2003-02-24 Jonathan Larmour <jifl@eCosCentric.com>
+
+ * cdl/usbs_sa11x0.cdl: Fix doc link.
+
+ * doc/usbs_sa11x0.sgml: Comment out DOCTYPE for now to allow building
+ with standard doc build.
+ Add an enclosing <reference> so it's structured better with standard
+ doc build.
+
+2002-02-11 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Add a handler for the SET_INTERFACE standard control message.
+ This should not be needed, but appears to avoid hardware problems
+ when a compliance testing program sends certain requests. Also
+ improve the handling of halted endpoints since the hardware
+ does not allow transfers to be aborted.
+
+2002-01-23 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Add missing assertions for non-NULL buffers
+ Try to improve the behaviour when the host sends data before
+ the target is ready. The hardware is not capable of handling
+ this situation, but some recovery is possible some of the time.
+
+2001-09-14 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Update support for USB testing
+
+2001-08-06 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Add initial support for USB testing.
+
+2001-05-21 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c (usbs_sa11x0_ep2_dsr):
+ Fix the boundary condition where the transmission is an exact
+ multiple of 64 bytes. ep2_process_packet() already did the
+ right thing but was not getting called.
+ Also, some cosmetic changes to the receive code for the
+ same boundary condition. These actually have no effect
+ because of the hardware, but may be useful for other
+ people writing USB device drivers.
+
+2001-04-05 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c (usbs_sa11x0_ep0_fill_fifo):
+ Set the DATA_END and IN_READY bits in one operation.
+ This seems to avoid problems when the target needs to send
+ back a zero-length control packet.
+
+2001-02-02 Bart Veer <bartv@redhat.com>
+
+ * cdl/usbs_sa11x0.cdl:
+ Add doc property to the html
+
+ * doc/usbs_sa11x0.sgml, devs-usbs-sa11x0.html:
+ Incorporate changes from docs department, regenerate html
+
+2001-01-25 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0_data.cxx:
+ * cdl/usbs_sa11x0.cdl:
+ Devtab entries were never actually being built - and did not
+ build...
+
+ * cdl/usbs_sa11x0.cdl:
+ Sort out the dependencies for minimal environments such as
+ RedBoot.
+
+2001-01-24 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Invoke additional platform-specific initialization, if defined
+ via <cyg/hal/hal_io.h> and CYGBLD_HAL_PLATFORM_IO_H
+
+2001-01-22 Bart Veer <bartv@redhat.com>
+
+ * doc/usbs_sa11x0.sgml, doc/makefile:
+ Added documentation.
+
+2001-01-16 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Make sure that the resume interrupt source is enabled,
+ even when the USB bus is not suspended. For some reason
+ this makes it possible to disconnect and reconnect.
+
+2001-01-16 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Use the HAL macros for virtual->physical address translation
+ Update poll() and start() to match the documentation
+ Fix the handling of control messages affecting endpoints 1 and 2
+ if those endpoints are not currently configured.
+
+
+2001-01-02 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Change ep2_tx_packet() to start the DMA operation after the
+ UDC packet-complete bit has been set. This avoids a very high
+ error rate. Add some scheduler locking to eliminate a resulting
+ race condition, and sort out the error handling to match.
+ Comment out some assertions relating to spurious interrupts, which
+ have been observed. Instead the code now recovers from these.
+
+2000-12-15 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Add debug code for tracking stats and simulating failures.
+ Change the EP1 code to switch between DMA channels A and B as
+ required, rather than always using channel A. This is more robust
+ and was needed for the failure simulation.
+
+ Make the ep1 packet processing code robust in case an unexpected
+ failure occurs during its invocation. It is not clear how this
+ can ever happen, but on a couple of occasions it did and caused an
+ infinite loop.
+
+2000-11-30 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Fix DMA_CONTROL_CLEAR_ALL constant, it was ignoring START_B
+ In ep2_init(), separate out the fifo write and the IN_SIZE
+ write to avoid a hardware problem.
+
+2000-11-29 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Disable some debugging features and add retries when manipulating
+ certain DMA registers - needed with some Silicon revisions.
+
+2000-11-28 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ More rewriting, plus implementing the endpoint halt support.
+
+2000-11-24 Bart Veer <bartv@redhat.com>
+
+ * src/usbs_sa11x0.c:
+ Clean up some of the debugging.
+ Largely rewrite the endpoint 2 support to try and get it working
+ Transmit a runt packet during initialization to work around
+ hardware problem.
+
+2000-11-22 Bart Veer <bartv@redhat.com>
+
+ * include/usbs_sa11x0.h: Fix nested #include protection
+
+2000-11-21 Bart Veer <bartv@redhat.com>
+
+ * First check-in of eCos USB support.
+
+//===========================================================================
+// ####GPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 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####
+//===========================================================================
diff --git a/ecos/packages/devs/usb/sa11x0/current/cdl/usbs_sa11x0.cdl b/ecos/packages/devs/usb/sa11x0/current/cdl/usbs_sa11x0.cdl
new file mode 100644
index 0000000..704319f
--- /dev/null
+++ b/ecos/packages/devs/usb/sa11x0/current/cdl/usbs_sa11x0.cdl
@@ -0,0 +1,202 @@
+# ====================================================================
+#
+# usbs_sa11x0.cdl
+#
+# SA11X0 USB support.
+#
+# ====================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 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): bartv
+# Original data: bartv
+# Contributors:
+# Date: 2000-10-04
+#
+#####DESCRIPTIONEND####
+# ====================================================================
+
+cdl_package CYGPKG_DEVS_USB_SA11X0 {
+ display "SA11X0 USB Device Driver"
+ include_dir "cyg/io/usb"
+ parent CYGPKG_USB
+ implements CYGHWR_IO_USB_SLAVE
+ doc ref/devs-usb-sa11x0.html
+
+ # Make sure that we are running on the right hardware.
+ requires CYGPKG_HAL_ARM
+ requires CYGPKG_HAL_ARM_SA11X0
+
+
+ description "
+ The on-chip serial port 0 on the SA11X0 implements a USB
+ device controller, facilitating the use of this processor
+ in USB peripherals. This package provides a suitable eCos
+ device driver."
+
+
+ cdl_option CYGFUN_DEVS_USB_SA11X0_EP0 {
+ display "Support the control endpoint 0"
+ default_value CYGINT_IO_USB_SLAVE_CLIENTS
+ # And the USB support packages
+ requires CYGPKG_IO_USB CYGPKG_IO_USB_SLAVE
+ compile usbs_sa11x0.c
+ compile -library=libextras.a usbs_sa11x0_data.cxx
+ description "
+ Enable support for endpoint 0. If this support is disabled
+ then the entire USB port is unusable."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_SA11X0_EP0_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 0"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If endpoint 0 will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and ioctl calls then a devtab entry is needed.
+ "
+ }
+
+ cdl_component CYGPKG_DEVS_USB_SA11X0_EP1 {
+ display "Support endpoint 1, used for host->slave communications"
+ implements CYGHWR_IO_USB_SLAVE_OUT_ENDPOINTS
+ requires CYGFUN_DEVS_USB_SA11X0_EP0
+ default_value CYGFUN_DEVS_USB_SA11X0_EP0
+ description "
+ In the SA11X0 USB implementation endpoint 1 can only be
+ used for host->slave communication. If the intended application
+ only involves slave->host transfers then the support for
+ endpoint 1 can be disabled. Note that this does not affect
+ control messages which always go via endpoint 0."
+
+ cdl_option CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL {
+ display "Control DMA usage for endpoint 1"
+ flavor booldata
+ legal_values 0 to 5
+ default_value 4
+ description "
+ In the SA11X0 USB implementation endpoint 1 only has
+ a 20-byte fifo. If the application only involves
+ small transfers then this may prove sufficient, but
+ for larger transfers the use of a DMA engine is
+ mandated. This configuration option allows the
+ use of DMA engine to be disabled or enabled, and the
+ specific DMA channel to be selected. The SA11X0
+ supports 6 DMA channels numbered 0 to 5. If DMA
+ is enabled for endpoint 1 then the selected channel
+ cannot be used by any other code."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_SA11X0_EP1_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 1"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If endpoint 1 will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and read calls then a devtab entry is needed.
+ "
+ }
+ }
+
+ cdl_component CYGPKG_DEVS_USB_SA11X0_EP2 {
+ display "Support endpoint 2, used for slave->host communications"
+ implements CYGHWR_IO_USB_SLAVE_IN_ENDPOINTS
+ requires CYGFUN_DEVS_USB_SA11X0_EP0
+ default_value CYGFUN_DEVS_USB_SA11X0_EP0
+ description "
+ In the SA11X0 USB implementation endpoint 2 can only be
+ used for slave->host communication. If the intended application
+ only involves host->slave transfers then the support for
+ endpoint 2 can be disabled. Note that this does not affect
+ control messages which always go via endpoint 0."
+
+ cdl_option CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL {
+ display "Control DMA usage for endpoint 2"
+ flavor booldata
+ legal_values 0 to 5
+ default_value 5
+ description "
+ In the SA11X0 USB implementation endpoint 2 only has
+ a 16-byte fifo. If the application only involves
+ small transfers then this may prove sufficient, but
+ for larger transfers the use of a DMA engine is
+ mandated. This configuration option allows the
+ use of DMA engine to be disabled or enabled, and the
+ specific DMA channel to be selected. The SA11X0
+ supports 6 DMA channels numbered 0 to 5. If DMA
+ is enabled for endpoint 2 then the selected channel
+ cannot be used by any other code."
+ }
+
+ cdl_option CYGVAR_DEVS_USB_SA11X0_EP2_DEVTAB_ENTRY {
+ display "Provide a devtab entry for endpoint 2"
+ default_value CYGGLO_IO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES
+ requires CYGPKG_IO
+ description "
+ If endpoint 2 will only be accessed via the low-level
+ USB-specific calls then there is no need for an entry
+ in the device table, saving some memory. If the
+ application intends to access the endpoint by means
+ of open and write calls then a devtab entry is needed.
+ "
+ }
+ }
+
+ cdl_option CYGDAT_DEVS_USB_SA11X0_DEVTAB_BASENAME {
+ display "Base name for devtab entries"
+ flavor data
+ active_if { CYGVAR_DEVS_USB_SA11X0_EP0_DEVTAB_ENTRY ||
+ CYGVAR_DEVS_USB_SA11X0_EP1_DEVTAB_ENTRY ||
+ CYGVAR_DEVS_USB_SA11X0_EP2_DEVTAB_ENTRY
+ }
+ default_value { "\"/dev/usbs\"" }
+ description "
+ If the SA11X0 USB device driver package provides devtab
+ entries for any of the endpoints then this option gives
+ control over the names of these entries. By default the
+ endpoints will be called \"/dev/usbs0c\", \"/dev/usbs1r\"
+ and \"/dev/usbs2w\" (assuming all three endpoints are
+ enabled. The common part \"/dev/usbs\" is determined
+ by this configuration option. It may be necessary to
+ change this if there are multiple USB slave-side
+ devices on the target hardware to prevent a name clash.
+ "
+ }
+}
diff --git a/ecos/packages/devs/usb/sa11x0/current/doc/devs-usb-sa11x0.html b/ecos/packages/devs/usb/sa11x0/current/doc/devs-usb-sa11x0.html
new file mode 100644
index 0000000..4c3791f
--- /dev/null
+++ b/ecos/packages/devs/usb/sa11x0/current/doc/devs-usb-sa11x0.html
@@ -0,0 +1,341 @@
+<!-- Copyright (C) 2001 Free Software Foundation, Inc. -->
+<!-- This material may be distributed only subject to the terms -->
+<!-- and conditions set forth in the Open Publication License, v1.0 -->
+<!-- or later (the latest version is presently available at -->
+<!-- http://www.opencontent.org/openpub/) -->
+<!-- Distribution of substantively modified versions of this -->
+<!-- document is prohibited without the explicit permission of the -->
+<!-- copyright holder. -->
+<!-- Distribution of the work or derivative of the work in any -->
+<!-- standard (paper) book form is prohibited unless prior -->
+<!-- permission obtained from the copyright holder -->
+<HTML>
+<HEAD>
+<TITLE>
+SA11X0 USB Device Driver</TITLE>
+<META
+NAME="GENERATOR"
+CONTENT="Modular DocBook HTML Stylesheet Version 1.54"></HEAD>
+<BODY
+CLASS="REFENTRY"
+BGCOLOR="#FFFFFF"
+TEXT="#000000"
+LINK="#0000FF"
+VLINK="#840084"
+ALINK="#0000FF">
+<H1>
+<A
+NAME="DEVS-USB-SA11X0">
+SA11X0 USB Device Driver</A>
+</H1>
+<DIV
+CLASS="REFNAMEDIV">
+<A
+NAME="AEN4">
+</A>
+<H2>
+Name</H2>
+SA11X0 USB Support&nbsp;--&nbsp;Device driver for the on-chip SA11X0 USB device</DIV>
+<DIV
+CLASS="REFSECT1">
+<A
+NAME="AEN7">
+</A>
+<H2>
+SA11X0 USB Hardware</H2>
+<P>
+The Intel StrongARM SA11x0 family of processors is supplied with an
+on-chip USB slave device, the UDC (USB Device Controller). This
+supports three endpoints. Endpoint 0 can only be used for control
+messages. Endpoint 1 can only be used for bulk transfers from host to
+peripheral. Endpoint 2 can only be used for bulk transfers from
+peripheral to host. Isochronous and interrupt transfers are not
+supported.</P>
+<DIV
+CLASS="CAUTION">
+<P>
+</P>
+<TABLE
+CLASS="CAUTION"
+BORDER="1"
+WIDTH="100%">
+<TR>
+<TD
+ALIGN="CENTER">
+<B>
+Caution</B>
+</TD>
+</TR>
+<TR>
+<TD
+ALIGN="LEFT">
+<P>
+Different revisions of the SA11x0 silicon have had various problems
+with the USB support. The device driver has been tested primarily
+against stepping B4 of the SA1110 processor, and may not function as
+expected with other revisions. Application developers should obtain
+the manufacturer's current errata sheets and specification updates.
+The B4 stepping still has a number of problems, but the device driver
+can work around these. However there is a penalty in terms of extra
+code, extra cpu cycles, and increased dispatch latency because extra
+processing is needed at DSR level. Interrupt latency should not be
+affected.</P>
+<P>
+There is one specific problem inherent in the UDC design of which
+application developers should be aware: the hardware cannot fully
+implement the USB standard for bulk transfers. A bulk transfer
+typically consists of some number of full-size 64-byte packets and is
+terminated by a packet less than the full size. If the amount of data
+transferred is an exact multiple of 64 bytes then this requires a
+terminating packet of 0 bytes of data (plus header and checksum). The
+SA11x0 USB hardware does not allow a 0-byte packet to be transmitted,
+so the device driver is forced to substitute a 1-byte packet and the
+host receives more data than expected. Protocol support is needed so
+that the appropriate host-side device driver can allow buffer space
+for the extra byte, detect when it gets sent, and discard it.
+Consequently certain standard USB class protocols cannot be
+implemented using the SA11x0, and therefore custom host-side device
+drivers will generally have to be provided, rather than re-using
+existing ones that understand the standard protocol.</P>
+</TD>
+</TR>
+</TABLE>
+</DIV>
+</DIV>
+<DIV
+CLASS="REFSECT1">
+<A
+NAME="AEN13">
+</A>
+<H2>
+Endpoint Data Structures</H2>
+<P>
+The SA11x0 USB device driver can provide up to three data structures
+corresponding to the three endpoints: a
+<SPAN
+CLASS="STRUCTNAME">
+usbs_control_endpoint</SPAN>
+ structure
+<TT
+CLASS="LITERAL">
+usbs_sa11x0_ep0</TT>
+; a
+<SPAN
+CLASS="STRUCTNAME">
+usbs_rx_endpoint</SPAN>
+
+<TT
+CLASS="LITERAL">
+usbs_sa11x0_ep1</TT>
+; and a
+<SPAN
+CLASS="STRUCTNAME">
+usbs_tx_endpoint</SPAN>
+
+<TT
+CLASS="LITERAL">
+usbs_sa11x0_ep2</TT>
+. The header file
+<TT
+CLASS="FILENAME">
+cyg/io/usb/usbs_sa11x0.h</TT>
+
+provides declarations for these.</P>
+<P>
+Not all applications will require support for all the endpoints. For
+example, if the intended use of the UDC only involves peripheral to
+host transfers then <TT
+CLASS="LITERAL">
+usbs_sa11x0_ep1</TT>
+ is redundant.
+The device driver provides configuration options to control the
+presence of each endpoint:</P>
+<P>
+</P>
+<OL
+TYPE="1">
+<LI>
+<P>
+Endpoint 0 is controlled by
+<TT
+CLASS="LITERAL">
+CYGFUN_DEVS_USB_SA11X0_EP0</TT>
+. This defaults to
+enabled if there are any higher-level packages that require USB
+hardware or if the global preference
+<TT
+CLASS="LITERAL">
+CYGGLO_IO_USB_SLAVE_APPLICATION</TT>
+ is enabled,
+otherwise it is disabled. Usually this has the desired effect. It may
+be necessary to override this in special circumstances, for example if
+the target board uses an external USB chip in preference to the UDC
+and it is that external chip's device driver that should be used
+rather than the on-chip UDC. It is not possible to disable endpoint 0
+and at the same time enable one or both of the other endpoints, since
+a USB device is only usable if it can process the standard control
+messages.</P>
+</LI>
+<LI>
+<P>
+Endpoint 1 is controlled by
+<TT
+CLASS="LITERAL">
+CYGPKG_DEVS_USB_SA11X0_EP1</TT>
+. By default it is
+enabled whenever endpoint 0 is enabled, but it can be disabled
+manually when not required.</P>
+</LI>
+<LI>
+<P>
+Similarly endpoint 2 is controlled by
+<TT
+CLASS="LITERAL">
+CYGPKG_DEVS_USB_SA11X0_EP2</TT>
+. This is also enabled by
+default whenever endpoint 0 is enabled, but it can be disabled manually.</P>
+</LI>
+</OL>
+<P>
+The SA11X0 USB device driver implements the interface specified by the
+common eCos USB Slave Support package. The documentation for that
+package should be consulted for further details. There is only one
+major deviation: when there is a peripheral to host transfer on
+endpoint 2 which is an exact multiple of the bulk transfer packet size
+(usually 64 bytes) the device driver has to pad the transfer with one
+extra byte. This is because of a hardware limitation: the UDC is
+incapable of transmitting 0-byte packets as required by the USB
+specification. Higher-level code, including the host-side device
+driver, needs to be aware of this and adapt accordingly.</P>
+<P>
+The device driver assumes a bulk packet size of 64 bytes, so this
+value should be used in the endpoint descriptors in the enumeration
+data provided by application code. There is experimental code
+for running with <A
+HREF="devs-usb-sa11x0.html#AEN58">
+DMA disabled</A>
+,
+in which case the packet size will be 16 bytes rather than 64.</P>
+</DIV>
+<DIV
+CLASS="REFSECT1">
+<A
+NAME="AEN39">
+</A>
+<H2>
+Devtab Entries</H2>
+<P>
+In addition to the endpoint data structures the SA11X0 USB device
+driver can also provide devtab entries for each endpoint. This allows
+higher-level code to use traditional I/O operations such as
+<TT
+CLASS="FUNCTION">
+open</TT>
+/<TT
+CLASS="FUNCTION">
+read</TT>
+/<TT
+CLASS="FUNCTION">
+write</TT>
+
+rather than the USB-specific non-blocking functions like
+<TT
+CLASS="FUNCTION">
+usbs_start_rx_buffer</TT>
+. These devtab entries are
+optional since they are not always required. The relevant
+configuration options are
+<TT
+CLASS="LITERAL">
+CYGVAR_DEVS_USB_SA11X0_EP0_DEVTAB_ENTRY</TT>
+,
+<TT
+CLASS="LITERAL">
+CYGVAR_DEVS_USB_SA11X0_EP1_DEVTAB_ENTRY</TT>
+ and
+<TT
+CLASS="LITERAL">
+CYGVAR_DEVS_USB_SA11X0_EP2_DEVTAB_ENTRY</TT>
+. By default
+these devtab entries are provided if the global preference
+<TT
+CLASS="LITERAL">
+CYGGLO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES</TT>
+ is enabled,
+which is usually the case. Obviously a devtab entry for a given
+endpoint will only be provided if the underlying endpoint is enabled.
+For example, there will not be a devtab entry for endpoint 1 if
+<TT
+CLASS="LITERAL">
+CYGPKG_DEVS_USB_SA11X0_EP1</TT>
+ is disabled.</P>
+<P>
+The names for the three devtab entries are determined by using a
+configurable base name and appending <TT
+CLASS="LITERAL">
+0c</TT>
+,
+<TT
+CLASS="LITERAL">
+1r</TT>
+ or <TT
+CLASS="LITERAL">
+2w</TT>
+. The base name is
+determined by the configuration option
+<TT
+CLASS="LITERAL">
+CYGDAT_DEVS_USB_SA11X0_DEVTAB_BASENAME</TT>
+ and has a
+default value of <TT
+CLASS="LITERAL">
+/dev/usbs</TT>
+, so the devtab entry for
+endpoint 1 would default to <TT
+CLASS="LITERAL">
+/dev/usbs1r</TT>
+. If the
+target hardware involves multiple USB devices then application
+developers may have to change the base name to prevent a name clash.</P>
+</DIV>
+<DIV
+CLASS="REFSECT1">
+<A
+NAME="AEN58">
+</A>
+<H2>
+DMA Engines</H2>
+<P>
+The SA11X0 UDC provides only limited fifos for bulk transfers on
+endpoints 1 and 2; smaller than the normal 64-byte bulk packet size.
+Therefore a typical transfer requires the use of DMA engines. The
+SA11x0 provides six DMA engines that can be used for this, and the
+endpoints require one each (assuming both endpoints are enabled). At
+the time of writing there is no arbitration mechanism to control
+access to the DMA engines. By default the device driver will use
+DMA engine 4 for endpoint 1 and DMA engine 5 for endpoint 2, and it
+assumes that no other code uses these particular engines.</P>
+<P>
+The exact DMA engines that will be used are determined by the
+configuration options
+<TT
+CLASS="LITERAL">
+CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL</TT>
+ and
+<TT
+CLASS="LITERAL">
+CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL</TT>
+. These
+options have the booldata flavor, allowing the use of DMA to be
+disabled completely in addition to controlling which DMA engines are
+used. If DMA is disabled then the device driver will attempt to
+work purely using the fifos, and the packet size will be limited to
+only 16 bytes. This limit should be reflected in the appropriate
+endpoint descriptors in the enumeration data. The code for driving the
+endpoints without DMA should be considered experimental. At best it
+will be suitable only for applications where the amount of data
+transferred is relatively small, because four times as many interrupts
+will be raised and performance will suffer accordingly.</P>
+</DIV>
+</BODY>
+</HTML>
diff --git a/ecos/packages/devs/usb/sa11x0/current/doc/makefile b/ecos/packages/devs/usb/sa11x0/current/doc/makefile
new file mode 100644
index 0000000..95cd4e9
--- /dev/null
+++ b/ecos/packages/devs/usb/sa11x0/current/doc/makefile
@@ -0,0 +1,54 @@
+#=============================================================================
+#
+# makefile
+#
+# For building the SA11x0 USB device driver documentation
+#
+#=============================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####
+## -------------------------------------------
+## This file is part of eCos, the Embedded Configurable Operating System.
+## Copyright (C) 1998, 1999, 2000, 2001, 2002 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): bartv
+# Date: 2001-01-11
+#####DESCRIPTIONEND####
+#=============================================================================
+
+TOPLEVEL := ../../../../..
+MAIN_SGML := usbs_sa11x0.sgml
+MAIN_HTML := devs-usb-sa11x0.html
+MAIN_PDF := devs-usb-sa11x0.pdf
+OTHER_SGML :=
+PICTURES :=
+
+include $(TOPLEVEL)/pkgconf/rules.doc
diff --git a/ecos/packages/devs/usb/sa11x0/current/doc/usbs_sa11x0.sgml b/ecos/packages/devs/usb/sa11x0/current/doc/usbs_sa11x0.sgml
new file mode 100644
index 0000000..cb6432c
--- /dev/null
+++ b/ecos/packages/devs/usb/sa11x0/current/doc/usbs_sa11x0.sgml
@@ -0,0 +1,234 @@
+<!-- DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" -->
+
+<!-- {{{ Banner -->
+
+<!-- =============================================================== -->
+<!-- -->
+<!-- usbs_sa11x0.sgml -->
+<!-- -->
+<!-- Documentation for the SA11x0 USB Device Driver. -->
+<!-- -->
+<!-- =============================================================== -->
+<!-- ####ECOSDOCCOPYRIGHTBEGIN#### -->
+<!-- =============================================================== -->
+<!-- Copyright (C) 2001, 2002 Free Software Foundation, Inc. -->
+<!-- This material may be distributed only subject to the terms -->
+<!-- and conditions set forth in the Open Publication License, v1.0 -->
+<!-- or later (the latest version is presently available at -->
+<!-- http://www.opencontent.org/openpub/) -->
+<!-- Distribution of the work or derivative of the work in any -->
+<!-- standard (paper) book form is prohibited unless prior -->
+<!-- permission obtained from the copyright holder -->
+<!-- =============================================================== -->
+<!-- ####ECOSDOCCOPYRIGHTEND#### -->
+<!-- =============================================================== -->
+<!-- #####DESCRIPTIONBEGIN#### -->
+<!-- -->
+<!-- Author(s): bartv -->
+<!-- Contact(s): bartv -->
+<!-- Date: 2001/01/11 -->
+<!-- Version: 0.01 -->
+<!-- -->
+<!-- ####DESCRIPTIONEND#### -->
+<!-- =============================================================== -->
+
+<!-- }}} -->
+
+<part id="devs-usb-sa11x0-ref">
+<!-- reference id="devs-usb-sa11x0-ref" -->
+ <title>SA11X0 USB Device Driver</title>
+
+<refentry id="devs-usb-sa11x0">
+<refmeta>
+<refentrytitle>SA11X0 USB Device Driver</refentrytitle>
+</refmeta>
+<refnamediv>
+<refname>SA11X0 USB Support</refname>
+<refpurpose>Device driver for the on-chip SA11X0 USB device</refpurpose>
+</refnamediv>
+
+<refsect1><title>SA11X0 USB Hardware</title>
+<para>
+The Intel StrongARM SA11x0 family of processors is supplied with an
+on-chip USB slave device, the UDC (USB Device Controller). This
+supports three endpoints. Endpoint 0 can only be used for control
+messages. Endpoint 1 can only be used for bulk transfers from host to
+peripheral. Endpoint 2 can only be used for bulk transfers from
+peripheral to host. Isochronous and interrupt transfers are not
+supported.
+</para>
+<caution>
+<para>
+Different revisions of the SA11x0 silicon have had various problems
+with the USB support. The device driver has been tested primarily
+against stepping B4 of the SA1110 processor, and may not function as
+expected with other revisions. Application developers should obtain
+the manufacturer's current errata sheets and specification updates.
+The B4 stepping still has a number of problems, but the device driver
+can work around these. However there is a penalty in terms of extra
+code, extra cpu cycles, and increased dispatch latency because extra
+processing is needed at DSR level. Interrupt latency should not be
+affected.
+</para>
+<para>
+There is one specific problem inherent in the UDC design of which
+application developers should be aware: the hardware cannot fully
+implement the USB standard for bulk transfers. A bulk transfer
+typically consists of some number of full-size 64-byte packets and is
+terminated by a packet less than the full size. If the amount of data
+transferred is an exact multiple of 64 bytes then this requires a
+terminating packet of 0 bytes of data (plus header and checksum). The
+SA11x0 USB hardware does not allow a 0-byte packet to be transmitted,
+so the device driver is forced to substitute a 1-byte packet and the
+host receives more data than expected. Protocol support is needed so
+that the appropriate host-side device driver can allow buffer space
+for the extra byte, detect when it gets sent, and discard it.
+Consequently certain standard USB class protocols cannot be
+implemented using the SA11x0, and therefore custom host-side device
+drivers will generally have to be provided, rather than re-using
+existing ones that understand the standard protocol.
+</para>
+</caution>
+</refsect1>
+
+<refsect1><title>Endpoint Data Structures</title>
+<para>
+The SA11x0 USB device driver can provide up to three data structures
+corresponding to the three endpoints: a
+<structname>usbs_control_endpoint</structname> structure
+<literal>usbs_sa11x0_ep0</literal>; a
+<structname>usbs_rx_endpoint</structname>
+<literal>usbs_sa11x0_ep1</literal>; and a
+<structname>usbs_tx_endpoint</structname>
+<literal>usbs_sa11x0_ep2</literal>. The header file
+<filename class="headerfile">cyg/io/usb/usbs_sa11x0.h</filename>
+provides declarations for these.
+</para>
+<para>
+Not all applications will require support for all the endpoints. For
+example, if the intended use of the UDC only involves peripheral to
+host transfers then <literal>usbs_sa11x0_ep1</literal> is redundant.
+The device driver provides configuration options to control the
+presence of each endpoint:
+</para>
+<orderedlist>
+<listitem>
+<para>
+Endpoint 0 is controlled by
+<literal>CYGFUN_DEVS_USB_SA11X0_EP0</literal>. This defaults to
+enabled if there are any higher-level packages that require USB
+hardware or if the global preference
+<literal>CYGGLO_IO_USB_SLAVE_APPLICATION</literal> is enabled,
+otherwise it is disabled. Usually this has the desired effect. It may
+be necessary to override this in special circumstances, for example if
+the target board uses an external USB chip in preference to the UDC
+and it is that external chip's device driver that should be used
+rather than the on-chip UDC. It is not possible to disable endpoint 0
+and at the same time enable one or both of the other endpoints, since
+a USB device is only usable if it can process the standard control
+messages.
+</para>
+</listitem>
+<listitem>
+<para>
+Endpoint 1 is controlled by
+<literal>CYGPKG_DEVS_USB_SA11X0_EP1</literal>. By default it is
+enabled whenever endpoint 0 is enabled, but it can be disabled
+manually when not required.
+</para>
+</listitem>
+<listitem>
+<para>
+Similarly endpoint 2 is controlled by
+<literal>CYGPKG_DEVS_USB_SA11X0_EP2</literal>. This is also enabled by
+default whenever endpoint 0 is enabled, but it can be disabled manually.
+</para>
+</listitem>
+</orderedlist>
+<para>
+The SA11X0 USB device driver implements the interface specified by the
+common eCos USB Slave Support package. The documentation for that
+package should be consulted for further details. There is only one
+major deviation: when there is a peripheral to host transfer on
+endpoint 2 which is an exact multiple of the bulk transfer packet size
+(usually 64 bytes) the device driver has to pad the transfer with one
+extra byte. This is because of a hardware limitation: the UDC is
+incapable of transmitting 0-byte packets as required by the USB
+specification. Higher-level code, including the host-side device
+driver, needs to be aware of this and adapt accordingly.
+</para>
+<para>
+The device driver assumes a bulk packet size of 64 bytes, so this
+value should be used in the endpoint descriptors in the enumeration
+data provided by application code. There is experimental code
+for running with <link linkend="usbs-sa11x0-dma">DMA disabled</link>,
+in which case the packet size will be 16 bytes rather than 64.
+</para>
+</refsect1>
+
+<refsect1><title>Devtab Entries</title>
+<para>
+In addition to the endpoint data structures the SA11X0 USB device
+driver can also provide devtab entries for each endpoint. This allows
+higher-level code to use traditional I/O operations such as
+<function>open</function>/<function>read</function>/<function>write</function>
+rather than the USB-specific non-blocking functions like
+<function>usbs_start_rx_buffer</function>. These devtab entries are
+optional since they are not always required. The relevant
+configuration options are
+<literal>CYGVAR_DEVS_USB_SA11X0_EP0_DEVTAB_ENTRY</literal>,
+<literal>CYGVAR_DEVS_USB_SA11X0_EP1_DEVTAB_ENTRY</literal> and
+<literal>CYGVAR_DEVS_USB_SA11X0_EP2_DEVTAB_ENTRY</literal>. By default
+these devtab entries are provided if the global preference
+<literal>CYGGLO_USB_SLAVE_PROVIDE_DEVTAB_ENTRIES</literal> is enabled,
+which is usually the case. Obviously a devtab entry for a given
+endpoint will only be provided if the underlying endpoint is enabled.
+For example, there will not be a devtab entry for endpoint 1 if
+<literal>CYGPKG_DEVS_USB_SA11X0_EP1</literal> is disabled.
+</para>
+<para>
+The names for the three devtab entries are determined by using a
+configurable base name and appending <literal>0c</literal>,
+<literal>1r</literal> or <literal>2w</literal>. The base name is
+determined by the configuration option
+<literal>CYGDAT_DEVS_USB_SA11X0_DEVTAB_BASENAME</literal> and has a
+default value of <literal>/dev/usbs</literal>, so the devtab entry for
+endpoint 1 would default to <literal>/dev/usbs1r</literal>. If the
+target hardware involves multiple USB devices then application
+developers may have to change the base name to prevent a name clash.
+</para>
+</refsect1>
+
+<refsect1><title id="usbs-sa11x0-dma">DMA Engines</title>
+<para>
+The SA11X0 UDC provides only limited fifos for bulk transfers on
+endpoints 1 and 2; smaller than the normal 64-byte bulk packet size.
+Therefore a typical transfer requires the use of DMA engines. The
+SA11x0 provides six DMA engines that can be used for this, and the
+endpoints require one each (assuming both endpoints are enabled). At
+the time of writing there is no arbitration mechanism to control
+access to the DMA engines. By default the device driver will use
+DMA engine 4 for endpoint 1 and DMA engine 5 for endpoint 2, and it
+assumes that no other code uses these particular engines.
+</para>
+<para>
+The exact DMA engines that will be used are determined by the
+configuration options
+<literal>CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL</literal> and
+<literal>CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL</literal>. These
+options have the booldata flavor, allowing the use of DMA to be
+disabled completely in addition to controlling which DMA engines are
+used. If DMA is disabled then the device driver will attempt to
+work purely using the fifos, and the packet size will be limited to
+only 16 bytes. This limit should be reflected in the appropriate
+endpoint descriptors in the enumeration data. The code for driving the
+endpoints without DMA should be considered experimental. At best it
+will be suitable only for applications where the amount of data
+transferred is relatively small, because four times as many interrupts
+will be raised and performance will suffer accordingly.
+</para>
+</refsect1>
+
+</refentry>
+</part>
+<!-- /reference -->
diff --git a/ecos/packages/devs/usb/sa11x0/current/include/usbs_sa11x0.h b/ecos/packages/devs/usb/sa11x0/current/include/usbs_sa11x0.h
new file mode 100644
index 0000000..f2288cc
--- /dev/null
+++ b/ecos/packages/devs/usb/sa11x0/current/include/usbs_sa11x0.h
@@ -0,0 +1,73 @@
+#ifndef CYGONCE_USBS_SA11X0_H
+# define CYGONCE_USBS_SA11X0_H
+//==========================================================================
+//
+// include/usbs_sa11x0.h
+//
+// The interface exported by the SA11X0 USB device driver
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 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): bartv
+// Contributors: bartv
+// Date: 2000-10-04
+// Purpose:
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/io/usb/usbs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The SA11x0 family comes with on-chip USB slave support. This
+ * provides three endpoints. Endpoint 0 can only be used for control
+ * messages. Endpoints 1 and 2 can only be used for bulk transfers,
+ * host->slave for endpoint 1 and slave->host for endpoint 2.
+ */
+extern usbs_control_endpoint usbs_sa11x0_ep0;
+extern usbs_rx_endpoint usbs_sa11x0_ep1;
+extern usbs_tx_endpoint usbs_sa11x0_ep2;
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+
+#endif /* CYGONCE_USBS_SA11X0_H */
diff --git a/ecos/packages/devs/usb/sa11x0/current/src/usbs_sa11x0.c b/ecos/packages/devs/usb/sa11x0/current/src/usbs_sa11x0.c
new file mode 100644
index 0000000..56b105f
--- /dev/null
+++ b/ecos/packages/devs/usb/sa11x0/current/src/usbs_sa11x0.c
@@ -0,0 +1,2550 @@
+//==========================================================================
+//
+// usbs_sa11x0.c
+//
+// Device driver for the SA11x0 USB port.
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 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): bartv
+// Contributors: bartv
+// Date: 2000-10-04
+//
+// This code implements support for the on-chip USB port on the SA11x0
+// family of processors. The code has been developed on the SA1110 and
+// may or may not work on other members of the SA11x0 family. There
+// have problems with the USB support on certain revisions of the silicon,
+// so the errata sheet appropriate to the specific processor being used
+// should be consulted. There also appear to be problems which do not
+// appear on any errata, which this code attempts to work around.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/cyg_trac.h>
+#include <cyg/infra/diag.h>
+
+#include <pkgconf/hal_arm.h>
+#include <pkgconf/devs_usb_sa11x0.h>
+
+#include <cyg/hal/drv_api.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_cache.h>
+#include <cyg/hal/hal_sa11x0.h>
+#include <cyg/error/codes.h>
+
+#include <cyg/io/usb/usb.h>
+#include <cyg/io/usb/usbs.h>
+
+// Debugging support. By default this driver operates mostly at
+// DSR level, with the ISR doing a minimal amount of processing.
+// However is also possible to run most of the code at thread-level,
+// This is subject to some restrictions because the USB standard
+// imposes timing constraints, e.g. some control operations such
+// as SET-ADDRESS have to complete within 50ms. However it is
+// very useful for debugging, specifically it allows you to put
+// printf()'s in various places.
+//
+// Right now these configuration options are not exported to the
+// user because running at DSR level is likely to be good enough
+// for everybody not actively debugging this code. The options
+// could be exported if necessary.
+//#define CYGPKG_DEVS_USB_SA11X0_THREAD
+#undef CYGPKG_DEVS_USB_SA11X0_THREAD
+#ifdef CYGPKG_DEVS_USB_SA11X0_THREAD
+ // Default stack size should be CYGNUM_HAL_STACK_SIZE_TYPICAL
+# define CYGNUM_DEVS_USB_SA11X0_THREAD_STACK_SIZE 4096
+# define CYGNUM_DEVS_USB_SA11X0_THREAD_PRIORITY 7
+# include <cyg/kernel/kapi.h>
+#endif
+
+#if 0
+# define DBG(a) diag_printf a
+#else
+# define DBG(a)
+#endif
+
+#undef FAILURES
+#ifdef FAILURES
+static volatile int ep1_failure = 7;
+#endif
+
+#undef STATS
+#ifdef STATS
+int ep1_receives = 0;
+int ep1_errors = 0;
+int ep2_transmits = 0;
+int ep2_errors = 0;
+# define INCR_STAT(a) (a) += 1
+# define SET_STAT(a, b) (a) = (b)
+#else
+# define INCR_STAT(a)
+# define SET_STAT(a, b)
+#endif
+
+// ----------------------------------------------------------------------------
+// Serial port 0 on the SA11x0 provides a USB slave connection (aka a
+// USB device controller or UDC). The functionality is somewhat
+// limited, there are just three endpoints.
+//
+// Endpoint 0 can only be used for control messages. It has an 8 byte
+// fifo which cannot be connected to a DMA engine. Hence incoming
+// control packets have to be limited to 8 bytes by the enumeration
+// data. The endpoint has to be managed at a low-level, i.e. the
+// incoming request has to be extracted from the fifo, processed, and
+// any response put back into the fifo within the permitted USB
+// response times.
+//
+// Endpoint 1 can only be used for host->slave bulk OUT transfers. It
+// has a 20 byte receive fifo, and it can be hooked up to any of the
+// six DMA engines. Since bulk transfers will typically involve 64
+// byte packets, most applications will require the use of DMA.
+//
+// Endpoint 2 can only be used for slave-host bulk IN transfers. There
+// is a 16 byte transmit fifo so small messages can be transferred in
+// software. The fifo can also be hooked up to DMA, which is a more
+// likely scenario.
+//
+// Start with definitions of the hardware. The use of a structure and
+// a const base pointer should allow the compiler to do base/offset
+// addressing and keep the hardware base address in a register. This
+// is better than defining each hardware register via a separate
+// address. Although the registers are only a byte wide, the peripheral
+// bus only supports word accesses.
+//
+// The USBS_CONTROL etc. macros allow for an alternative way of
+// accessing the hardware if a better approach is presented, without
+// having to rewrite all the code. Macros that correspond to registers
+// are actually addresses, making it easier in the code to distinguish
+// them from bit values: the & and * operators will just cancel out.
+
+typedef struct usbs_sa11x0_hardware {
+ volatile int control;
+ volatile int address;
+ volatile int out_size;
+ volatile int in_size;
+ volatile int ep0_control;
+ volatile int ep1_control;
+ volatile int ep2_control;
+ volatile int ep0_data;
+ volatile int ep0_write_count;
+ int dummy1;
+ volatile int fifo;
+ int dummy2;
+ volatile int status;
+} usbs_sa11x0_hardware;
+
+static usbs_sa11x0_hardware* const usbs_sa11x0_base = (usbs_sa11x0_hardware* const) 0x80000000;
+#define USBS_CONTROL (&(usbs_sa11x0_base->control))
+#define USBS_ADDRESS (&(usbs_sa11x0_base->address))
+#define USBS_OUT_SIZE (&(usbs_sa11x0_base->out_size))
+#define USBS_IN_SIZE (&(usbs_sa11x0_base->in_size))
+#define EP0_CONTROL (&(usbs_sa11x0_base->ep0_control))
+#define EP1_CONTROL (&(usbs_sa11x0_base->ep1_control))
+#define EP2_CONTROL (&(usbs_sa11x0_base->ep2_control))
+#define EP0_DATA (&(usbs_sa11x0_base->ep0_data))
+#define EP0_WRITE_COUNT (&(usbs_sa11x0_base->ep0_write_count))
+#define EP1_DATA (&(usbs_sa11x0_base->fifo))
+#define EP2_DATA (&(usbs_sa11x0_base->fifo))
+#define USBS_STATUS (&(usbs_sa11x0_base->status))
+
+#define CONTROL_DISABLE (1 << 0)
+#define CONTROL_ACTIVE (1 << 1)
+// The meaning of bit 2 changed, see errata
+#define CONTROL_RESUME_INTR (1 << 2)
+#define CONTROL_EP0_INTR (1 << 3)
+#define CONTROL_EP1_INTR (1 << 4)
+#define CONTROL_EP2_INTR (1 << 5)
+// The meaning of bit 6 also changed, see errata
+#define CONTROL_SUSPEND_INTR (1 << 6)
+#define CONTROL_RESET_INTR (1 << 7)
+
+// Getting the control register settings right is a little bit tricky.
+// Bit 0 is the disable bit so touching that is dangerous, and the
+// other bits have inverted meanings i.e. 0 enables interrupts. The
+// following macro encapsulates this.
+#define CONTROL_ALL_INTR 0x00FC
+#define CONTROL_INTR_ENABLE(bits) ((~(bits)) & CONTROL_ALL_INTR)
+#define CONTROL_INTR_CLEAR(bits) ((bits) & CONTROL_ALL_INTR)
+
+// All the endpoint interrupt numbers can be handled en masse,
+// but some of the endpoints may be disabled.
+#if defined(CYGPKG_DEVS_USB_SA11X0_EP1) && defined(CYGPKG_DEVS_USB_SA11X0_EP2)
+# define CONTROL_EP_INTR_BITS (CONTROL_EP0_INTR | CONTROL_EP1_INTR | CONTROL_EP2_INTR)
+#elif defined(CYGPKG_DEVS_USB_SA11X0_EP1)
+# define CONTROL_EP_INTR_BITS (CONTROL_EP0_INTR | CONTROL_EP1_INTR)
+#elif defined(CYGPKG_DEVS_USB_SA11X0_EP2)
+# define CONTROL_EP_INTR_BITS (CONTROL_EP0_INTR | CONTROL_EP2_INTR)
+#else
+# define CONTROL_EP_INTR_BITS (CONTROL_EP0_INTR)
+#endif
+
+#define EP0_OUT_READY (1 << 0)
+#define EP0_IN_READY (1 << 1)
+#define EP0_SENT_STALL (1 << 2)
+#define EP0_FORCE_STALL (1 << 3)
+#define EP0_DATA_END (1 << 4)
+#define EP0_SETUP_END (1 << 5)
+#define EP0_SERVICED_OPR (1 << 6)
+#define EP0_SERVICED_SETUP_END (1 << 7)
+
+#define EP1_FIFO_SERVICE (1 << 0)
+#define EP1_PACKET_COMPLETE (1 << 1)
+#define EP1_PACKET_ERROR (1 << 2)
+#define EP1_SENT_STALL (1 << 3)
+#define EP1_FORCE_STALL (1 << 4)
+#define EP1_FIFO_NOT_EMPTY (1 << 5)
+
+#define EP2_FIFO_SERVICE (1 << 0)
+#define EP2_PACKET_COMPLETE (1 << 1)
+#define EP2_PACKET_ERROR (1 << 2)
+#define EP2_PACKET_UNDERRUN (1 << 3)
+#define EP2_SENT_STALL (1 << 4)
+#define EP2_FORCE_STALL (1 << 5)
+
+#define STATUS_EP0_INTR (1 << 0)
+#define STATUS_EP1_INTR (1 << 1)
+#define STATUS_EP2_INTR (1 << 2)
+#define STATUS_SUSPEND_INTR (1 << 3)
+#define STATUS_RESUME_INTR (1 << 4)
+#define STATUS_RESET_INTR (1 << 5)
+
+#define EP0_FIFO_SIZE 8
+#define EP0_MTU 8
+
+#define EP1_FIFO_SIZE 20
+#ifdef CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL
+# define EP1_MTU 64
+#else
+# define EP1_MTU 16
+#endif
+
+#define EP2_FIFO_SIZE 16
+#ifdef CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL
+# define EP2_MTU 64
+#else
+# define EP2_MTU 16
+#endif
+
+#if defined(CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL) || defined(CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL)
+typedef struct usbs_sa11x0_dma {
+ volatile int address;
+ volatile int control_set;
+ volatile int control_clear;
+ volatile int status;
+ volatile int buf_a_address; // Absolute, not remapped
+ volatile int buf_a_size;
+ volatile int buf_b_address; // Absolute, not remapped
+ volatile int buf_b_size;
+} usbs_sa11x0_dma;
+
+#define DMA_CONTROL_RUN (1 << 0)
+#define DMA_CONTROL_INTR_ENABLE (1 << 1)
+#define DMA_STATUS_ERROR (1 << 2)
+#define DMA_STATUS_DONE_A (1 << 3)
+#define DMA_CONTROL_START_A (1 << 4)
+#define DMA_STATUS_DONE_B (1 << 5)
+#define DMA_CONTROL_START_B (1 << 6)
+#define DMA_STATUS_BUFFER_IN_USE (1 << 7)
+// All the bits that are useful to clear. BUFFER_IN_USE is read-only.
+#define DMA_CONTROL_CLEAR_ALL (DMA_CONTROL_RUN | DMA_CONTROL_INTR_ENABLE | DMA_STATUS_ERROR | \
+ DMA_STATUS_DONE_A | DMA_CONTROL_START_A | DMA_STATUS_DONE_B | DMA_CONTROL_START_B)
+
+// The DMA engines operate eight-bytes at a time. This affects issues
+// such as alignment.
+#define DMA_BURST_SIZE 8
+
+// The DMA engines bypass the cache and MMU, accessing physical
+// memory directly. Newer HALS should provide appropriate macros.
+#ifndef HAL_VIRT_TO_PHYS_ADDRESS
+# error HAL macros for translating between virtual and physical memory are required.
+#endif
+
+// Make absolutely sure that the two endpoints use different
+// DMA channels. Right now this check cannot be done easily
+// at the CDL level.
+# if defined(CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL) && defined(CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL)
+# if (CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL == CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL)
+# error Different DMA channels must be selected for the two endpoints.
+# endif
+# endif
+
+# ifdef CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL
+static usbs_sa11x0_dma* const ep1_dma_base = (usbs_sa11x0_dma* const)(0xB0000000 | (0x20 * CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL));
+# define EP1_DMA_ADDRESS (&(ep1_dma_base->address))
+# define EP1_DMA_CONTROL_SET (&(ep1_dma_base->control_set))
+# define EP1_DMA_CONTROL_CLEAR (&(ep1_dma_base->control_clear))
+# define EP1_DMA_STATUS (&(ep1_dma_base->status))
+# define EP1_DMA_BUF_A_ADDRESS (&(ep1_dma_base->buf_a_address))
+# define EP1_DMA_BUF_A_SIZE (&(ep1_dma_base->buf_a_size))
+# define EP1_DMA_BUF_B_ADDRESS (&(ep1_dma_base->buf_b_address))
+# define EP1_DMA_BUF_B_SIZE (&(ep1_dma_base->buf_b_size))
+
+// The correct value for the DMA address register is fixed for USB transfers
+// See table 11.6 of the SA1110 Advanced Developer's Manual
+// Device datum width == 1 byte
+// Device burst size == 8 bytes
+// Device transfer direction == read (device->memory)
+// Endianness is controlled by the ARM architectural HAL package
+# ifdef CYGHWR_HAL_ARM_BIGENDIAN
+# define EP1_DMA_ADDRESS_VALUE (0x80000A00 | 0x10 | 0x0 | 0x4 | 0x2 | 0x1)
+# else
+# define EP1_DMA_ADDRESS_VALUE (0x80000A00 | 0x10 | 0x0 | 0x4 | 0x0 | 0x1)
+# endif
+# endif // EP1_DMA
+
+# ifdef CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL
+
+static usbs_sa11x0_dma* const ep2_dma_base = (usbs_sa11x0_dma* const)(0xB0000000 | (0x20 * CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL));
+# define EP2_DMA_ADDRESS (&(ep2_dma_base->address))
+# define EP2_DMA_CONTROL_SET (&(ep2_dma_base->control_set))
+# define EP2_DMA_CONTROL_CLEAR (&(ep2_dma_base->control_clear))
+# define EP2_DMA_STATUS (&(ep2_dma_base->status))
+# define EP2_DMA_BUF_A_ADDRESS (&(ep2_dma_base->buf_a_address))
+# define EP2_DMA_BUF_A_SIZE (&(ep2_dma_base->buf_a_size))
+# define EP2_DMA_BUF_B_ADDRESS (&(ep2_dma_base->buf_b_address))
+# define EP2_DMA_BUF_B_SIZE (&(ep2_dma_base->buf_b_size))
+
+# ifdef CYGHWR_HAL_ARM_BIGENDIAN
+# define EP2_DMA_ADDRESS_VALUE (0x80000A00 | 0x00 | 0x0 | 0x4 | 0x2 | 0x0)
+# else
+# define EP2_DMA_ADDRESS_VALUE (0x80000A00 | 0x00 | 0x0 | 0x4 | 0x0 | 0x0)
+# endif
+# endif // EP2_DMA
+
+#endif // EP1_DMA || EP2_DMA
+
+// ----------------------------------------------------------------------------
+// Static data. There is a data structure for each endpoint. The
+// implementation is essentially a private class that inherits from
+// common classes for control and data endpoints, but device drivers
+// are supposed to be written in C so some ugliness is required.
+//
+// Devtab entries are defined in usbs_sa11x0_data.cxx to make sure
+// that the linker does not garbage-collect them.
+
+// Support for the interrupt handling code.
+static cyg_interrupt usbs_sa11x0_intr_data;
+static cyg_handle_t usbs_sa11x0_intr_handle;
+static volatile int isr_status_bits = 0;
+
+// Endpoint 0 is always present, this module would not get compiled
+// otherwise.
+static void usbs_sa11x0_ep0_start(usbs_control_endpoint*);
+static void usbs_sa11x0_poll(usbs_control_endpoint*);
+
+typedef enum ep0_state {
+ EP0_STATE_IDLE = 0,
+ EP0_STATE_IN = 1,
+ EP0_STATE_OUT = 2
+} ep0_state;
+
+typedef struct ep0_impl {
+ usbs_control_endpoint common;
+ ep0_state ep_state;
+ int length;
+ int transmitted;
+} ep0_impl;
+
+static ep0_impl ep0 = {
+ common:
+ {
+ state: USBS_STATE_POWERED, // The hardware does not distinguish between detached, attached and powered.
+ enumeration_data: (usbs_enumeration_data*) 0,
+ start_fn: &usbs_sa11x0_ep0_start,
+ poll_fn: &usbs_sa11x0_poll,
+ interrupt_vector: SA11X0_IRQ_USB_SERVICE_REQUEST,
+ control_buffer: { 0, 0, 0, 0, 0, 0, 0, 0 },
+ state_change_fn: (void (*)(usbs_control_endpoint*, void*, usbs_state_change, int)) 0,
+ state_change_data: (void*) 0,
+ standard_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
+ standard_control_data: (void*) 0,
+ class_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
+ class_control_data: (void*) 0,
+ vendor_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
+ vendor_control_data: (void*) 0,
+ reserved_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
+ reserved_control_data: (void*) 0,
+ buffer: (unsigned char*) 0,
+ buffer_size: 0,
+ fill_buffer_fn: (void (*)(usbs_control_endpoint*)) 0,
+ fill_data: (void*) 0,
+ fill_index: 0,
+ complete_fn: (usbs_control_return (*)(usbs_control_endpoint*, int)) 0
+ },
+ ep_state: EP0_STATE_IDLE,
+ length: 0,
+ transmitted: 0
+};
+
+extern usbs_control_endpoint usbs_sa11x0_ep0 __attribute__((alias ("ep0")));
+
+// Endpoint 1 is optional. If the application only involves control
+// messages or only slave->host transfers then the endpoint 1
+// support can be disabled.
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP1
+
+typedef struct ep1_impl {
+ usbs_rx_endpoint common;
+ int fetched;
+ cyg_bool using_buf_a;
+} ep1_impl;
+
+static void ep1_start_rx(usbs_rx_endpoint*);
+static void ep1_set_halted(usbs_rx_endpoint*, cyg_bool);
+
+static ep1_impl ep1 = {
+ common: {
+ start_rx_fn: &ep1_start_rx,
+ set_halted_fn: &ep1_set_halted,
+ complete_fn: (void (*)(void*, int)) 0,
+ complete_data: (void*) 0,
+ buffer: (unsigned char*) 0,
+ buffer_size: 0,
+ halted: 0,
+ },
+ fetched: 0,
+ using_buf_a: 0
+};
+
+extern usbs_rx_endpoint usbs_sa11x0_ep1 __attribute__((alias ("ep1")));
+#endif
+
+// Endpoint 2 is optional. If the application only involves control
+// messages or only host->slave transfers then the endpoint 2 support
+// can be disabled.
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP2
+
+typedef struct ep2_impl {
+ usbs_tx_endpoint common;
+ int transmitted;
+ int pkt_size;
+} ep2_impl;
+
+static void ep2_start_tx(usbs_tx_endpoint*);
+static void ep2_set_halted(usbs_tx_endpoint*, cyg_bool);
+
+static ep2_impl ep2 = {
+ common: {
+ start_tx_fn: &ep2_start_tx,
+ set_halted_fn: &ep2_set_halted,
+ complete_fn: (void (*)(void*, int)) 0,
+ complete_data: (void*) 0,
+ buffer: (const unsigned char*) 0,
+ buffer_size: 0,
+ halted: 0,
+ },
+ transmitted: 0,
+ pkt_size: 0
+};
+
+extern usbs_tx_endpoint usbs_sa11x0_ep2 __attribute__ ((alias ("ep2")));
+
+#endif
+
+// ----------------------------------------------------------------------------
+// Hardware problem: experiments indicate that manipulating the USB
+// controller registers does not always work as expected. The control
+// fifo is especially badly affected, with e.g. writes just being lost
+// completely. It is necessary to work around these problems using
+// retry loops. MAX_RETRIES controls the total number of attempts to
+// access a register. MAX_CHECKS controls the number of times a
+// register is checked to determine whether or not the attempt has
+// been succesful. These constants are used to access the data fifo,
+// so MAX_RETRIES has to be > 20 bytes.
+#define MAX_RETRIES 32
+#define MAX_CHECKS 8
+
+// Write one or more bits to a register. This should result in some
+// bits ending up set and other bits ending up clear. Some register
+// bits are write-1-to-clear or may have side effects.
+static cyg_bool
+usbs_sa11x0_poke(volatile int* addr, int value, int should_be_set, int should_be_clear)
+{
+ cyg_bool result = false;
+ int retries, checks;
+
+ for (retries = 0; !result && (retries < MAX_RETRIES); retries++) {
+ *addr = value;
+ (void) *addr; // The first read is always invalid.
+ for (checks = 0; !result && (checks < MAX_CHECKS); checks++) {
+ int current_value = *addr;
+ if (should_be_set != (should_be_set & current_value)) {
+ continue;
+ }
+ if ((0 != should_be_clear) && (0 != (should_be_clear & current_value))) {
+ continue;
+ }
+ result = true;
+ }
+ }
+ if (!result) {
+ DBG(("usbs_sa11x0_poke failed: addr %x, value %x, should_be_set %x, should_be_clear %x, actual %x\n", \
+ (int) addr, value, should_be_set, should_be_clear, *addr));
+ }
+ return result;
+}
+
+// Write a whole value to a register, rather than just manipulating
+// individual bits.
+static cyg_bool
+usbs_sa11x0_poke_value(volatile int* addr, int value)
+{
+ cyg_bool result = false;
+ int retries, checks;
+
+ for (retries = 0; !result && (retries < MAX_RETRIES); retries++) {
+ *addr = value;
+ (void) *addr; // The first read is always invalid.
+ for (checks = 0; !result && (checks < MAX_CHECKS); checks++) {
+ if (value == *addr) {
+ result = true;
+ }
+ }
+ }
+ if (!result) {
+ DBG(("usbs_sa11x0_poke_value failed: addr %x, value %x, actual %x\n", (int) addr, value, *addr));
+ }
+ return result;
+}
+
+
+// ----------------------------------------------------------------------------
+// Control transfers
+//
+// Endpoint 0 is rather more complicated than the others. This is
+// partly due to the nature of the control protocol, for example it is
+// bidirectional and transfer sizes are unpredictable.
+//
+// The USB standard imposes some timing constraints on endpoint 0, see
+// section 9.2.6 of the spec. For example the set-address operation is
+// supposed to take at most 50ms. In general the timings are reasonably
+// generous so no special action is taken here. There could be problems
+// when debugging, but that is pretty much inevitable.
+//
+// It is necessary to maintain a state for the control endpoint, the
+// default state being idle. Control operations involve roughly the
+// following sequence of events:
+//
+// 1) the host transmits a special setup token, indicating the start
+// of a control operation and possibly cancelling any existing control
+// operation that may be in progress. USB peripherals cannot NAK this
+// even if they are busy.
+//
+// 2) the setup operation is followed by an eight-byte packet from the host
+// that describes the specific control operation. This fits into the
+// SA11X0's eight-byte control fifo. There will be an endpoint 0
+// interrupt with the out-packet-ready bit set. If the setup token
+// was sent while a previous control operation was also in progress
+// then the setup-end bit will be set as well.
+//
+// 3) the eight-byte packet is described in section 9.3 of the USB spec.
+// The first byte holds three fields, with the top bit indicating the
+// direction of subsequent data transfer. There are also two bytes
+// specifying the size of the subsequent transfer. Obviously the
+// packet also contains information such as the request type.
+//
+// If the specified size is zero then the endpoint will remain in
+// its idle state. Otherwise the endpoint will switch to either
+// IN or OUT state, depending on the direction of subsequent
+// transfers.
+//
+// 4) some standard control operations can be handled by the code
+// here. Set-address involves poking the address register and
+// a change of state. Set-feature and clear-feature on the
+// data endpoints can be used in conjunction with endpoint-halt.
+// Get-status on the data endpoints tests the halt condition.
+// It is also possible for the hardware-specific code to
+// implement set-feature, clear-feature and get-status
+// for the device as a whole since the SA11x0 always has to
+// be self-powered and is incapable of initiating a remote
+// wakeup.
+//
+// Other standard control operations will be handled by the
+// application-specific installed handler, if any, or by the
+// default handler usbs_handle_standard_control(). Class-specific
+// and vendor-specific functions require appropriate handlers to be
+// installed as well, If a particular request is not recognized
+// then a stall condition should be raised. This will not prevent
+// subsequent control operations, just the current one.
+//
+// Data transfers on endpoint 0 involve at most eight bytes at
+// a time. More data will only be accepted if the out-packet-ready
+// bit has been cleared via the serviced-opr bit, with the
+// hardware nak'ing OUT requests. To send data back to the host
+// the FIFO should be filled and then the in-packet-ready bit
+// should be set.
+//
+// It looks like processing all control packets at DSR level should be
+// sufficient. During the data phase the hardware will NAK IN and
+// OUT requests if the fifo is still empty/full, so timing is not
+// an issue. Timing after receipt of the initial control message
+// may be more important, e.g. the 50ms upper limit on processing
+// the set-address control message, but this should still be ok.
+// This decision may have to be re-examined in the light of
+// experience.
+
+// Init may get called during system startup or following a reset.
+// During startup no work is needed since the hardware will
+// have been reset and everything should be fine. After a reset
+// the hardware will also be ok but there may be state information
+// in ep0 that needs to be reset.
+static void
+usbs_sa11x0_ep0_init(void)
+{
+ if ((EP0_STATE_IDLE != ep0.ep_state) &&
+ ((usbs_control_return (*)(usbs_control_endpoint*, int)) 0 != ep0.common.complete_fn)) {
+ (*ep0.common.complete_fn)(&ep0.common, -EPIPE);
+ }
+ ep0.common.state = USBS_STATE_POWERED;
+ memset(ep0.common.control_buffer, 0, 8);
+ ep0.common.buffer = (unsigned char*) 0;
+ ep0.common.buffer_size = 0;
+ ep0.common.fill_buffer_fn = (void (*)(usbs_control_endpoint*)) 0;
+ ep0.common.fill_data = (void*) 0;
+ ep0.common.fill_index = 0;
+ ep0.common.complete_fn = (usbs_control_return (*)(usbs_control_endpoint*, int)) 0;
+ ep0.ep_state = EP0_STATE_IDLE;
+ ep0.length = 0;
+ ep0.transmitted = 0;
+}
+
+// The start function is called by higher-level code when things have
+// been set up, i.e. the enumeration data is available, appropriate
+// handlers have been installed for the different types of control
+// messages, and communication with the host is allowed to start. The
+// next event that should happen is a reset operation from the host,
+// so all other interrupts should be blocked. However it is likely
+// that the hardware will detect a suspend state before the reset
+// arrives, and hence the reset will act as a resume as well as a
+// reset.
+static void
+usbs_sa11x0_ep0_start(usbs_control_endpoint* endpoint)
+{
+ CYG_ASSERT( endpoint == &ep0.common, "USB startup involves the wrong endpoint");
+
+ // Activate the hardware. Write a 0 to the enable/disable bit 0.
+ // Bit 1 is read-only. The other bits are set to 1 to disable
+ // the corresponding interrupt source.
+ usbs_sa11x0_poke(USBS_CONTROL, CONTROL_ALL_INTR, CONTROL_ALL_INTR, 0);
+
+ // If there is additional platform-specific initialization to
+ // perform, do it now. This macro can come from the platform HAL.
+#ifdef SA11X0_USB_PLATFORM_INIT
+ SA11X0_USB_PLATFORM_INIT;
+#endif
+
+ // Clear any pending interrupts. There should not be any, but just
+ // in case. Note: passing 0x00FF as the should_be_clear argument
+ // is a race condition, an external event can happen at any time,
+ // so we may loop unnecessarily and lose an interrupt. However
+ // the initial reset should last for 10ms.
+ usbs_sa11x0_poke(USBS_STATUS, 0x00FF, 0x00, 0x00FF);
+
+ // The only interrupt really of interest right now is reset, but
+ // it is likely to be preceded by a resume.
+ usbs_sa11x0_poke(USBS_CONTROL,
+ CONTROL_INTR_ENABLE(CONTROL_RESET_INTR | CONTROL_RESUME_INTR),
+ 0,
+ CONTROL_INTR_CLEAR(CONTROL_RESET_INTR | CONTROL_RESUME_INTR));
+}
+
+
+// Filling the fifo with a reply to the host. This can be called
+// immediately at the end of a control message, to prepare for
+// the next IN token. It will also get called after each subsequent
+// IN operation when the fifo has been emptied.
+//
+// Experiments have indicated serious problems with the control fifo:
+// some writes to the fifo just get lost completely. The failure rate
+// is sufficiently high that more often than not the host will be
+// unable to read all the enumeration data. However, the write-count
+// register appears to give a valid indication of the current fifo
+// contents. This means the code can retry stuffing a particular byte
+// into the fifo until the write-count goes up.
+
+static void
+usbs_sa11x0_ep0_fill_fifo(void)
+{
+ cyg_bool ok = true;
+ int filled = 0;
+ int max;
+ int fifo_count = *EP0_WRITE_COUNT;
+ int bits_to_set = 0;
+
+ // The host can interrupt the current control message at any time
+ // with a new one. In practice this is unlikely, things could get
+ // rather confused on the host side. However if a control message
+ // has been received then the fifo should obviously not be filled.
+ // A new control message is indicated by the SETUP_END bit.
+ //
+ // The hardware design means that there is a race condition: the
+ // new control message can come in at any time, even in the middle
+ // of filling the fifo. Checking the SETUP_END more often would
+ // reduce the probability of things getting messed up, but not
+ // eliminate it.
+ //
+ // There is a check for SETUP_END at the start of the DSR, so
+ // the setting of this bit should have resulted in another ISR
+ // and another DSR being scheduled. Hence there is no need for
+ // special action here.
+ if (0 != (*EP0_CONTROL & EP0_SETUP_END)) {
+ DBG(("EP0_fill_fifo(), interrupted by SETUP_END\n"));
+ return;
+ }
+
+ // There should never be any data in the fifo. Any such data could
+ // be the remnant of a previous transfer to the host, but that
+ // should all have gone out already. Alternatively it could be
+ // incoming data, but that means a new control message.
+ if (0 != fifo_count) {
+ DBG(("EP0_fill_fifo(), fifo already contains %d bytes", fifo_count));
+ return;
+ }
+
+ // The IN_READY bit should never be set on entry. It can only get
+ // set by a previous call to fill_fifo(), and the data should
+ // have gone out before we get back here.
+ if (0 != (*EP0_CONTROL & EP0_IN_READY)) {
+ DBG(("EP0 fill_fifo(), in-packet-ready bit already set, state %x\n", *EP0_CONTROL));
+ return;
+ }
+
+ // Now put up to another eight bytes into the fifo.
+ max = ((ep0.length - ep0.transmitted) > EP0_FIFO_SIZE) ? EP0_FIFO_SIZE : (ep0.length - ep0.transmitted);
+ while (ok && (filled < max)) {
+ if (0 != ep0.common.buffer_size) {
+ int datum;
+ int retries, checks;
+ cyg_bool written;
+
+ datum = *ep0.common.buffer++;
+ ep0.common.buffer_size--;
+ written = false;
+
+ for (retries = 0; ok && !written && (retries < MAX_RETRIES); retries++) {
+ if (filled != *EP0_WRITE_COUNT) {
+ DBG(("EP0 fill_fifo, inconsistency, written %d but write count %d\n", filled, *EP0_WRITE_COUNT));
+ ok = false;
+ }
+ *EP0_DATA = datum;
+ // The write-count may take a few cycles to settle down.
+ for (checks = 0; !written && (checks < MAX_CHECKS); checks++) {
+ if (filled < *EP0_WRITE_COUNT) {
+ filled++;
+ written = true;
+ // DBG(("Transferred %d byte (%x) after %d checks, %d retries\n", filled - 1, datum, checks, retries));
+ }
+ }
+ }
+ } else if ((void (*)(usbs_control_endpoint*))0 != ep0.common.fill_buffer_fn) {
+ (*ep0.common.fill_buffer_fn)(&ep0.common);
+ } else {
+ break;
+ }
+ }
+
+ // At this point either it has proved impossible to fill the fifo,
+ // e.g. because of a new control message, or up to another eight
+ // bytes have been sent.
+ if (!ok) {
+ if (0 == (EP0_SETUP_END & *EP0_CONTROL)) {
+ // There is something seriously wrong.
+ DBG(("ep0_fill_fifo(), failed, only filled %d bytes, status %x\n", filled, *EP0_CONTROL));
+ usbs_sa11x0_poke(EP0_CONTROL, EP0_FORCE_STALL, EP0_FORCE_STALL, 0);
+ }
+ return;
+ }
+
+ // The following conditions are possible:
+ // 1) amount transferred == amount requested, transfer complete.
+ // 2) amount transferred < amount requested, this fill involved
+ // <eight bytes, transfer complete by definition of the protocol.
+ // 3) amount transferred < amount requested but exactly eight
+ // bytes were sent this time. It will be necessary to send
+ // another packet of zero bytes to complete the transfer.
+ ep0.transmitted += filled;
+ if ((ep0.transmitted == ep0.length) || (filled < EP0_FIFO_SIZE)) {
+
+ ep0.ep_state = EP0_STATE_IDLE;
+ if ((usbs_control_return (*)(usbs_control_endpoint*, int))0 != ep0.common.complete_fn) {
+ (void) (*ep0.common.complete_fn)(&ep0.common, 0);
+ }
+ ep0.common.buffer = (unsigned char*) 0;
+ ep0.common.buffer_size = 0;
+ ep0.common.fill_buffer_fn = (void (*)(usbs_control_endpoint*)) 0;
+
+ // This informs the hardware that the control message has been
+ // handled.
+ bits_to_set = EP0_DATA_END;
+ }
+
+ // This allows another IN operation to empty the fifo.
+ bits_to_set |= EP0_IN_READY;
+ usbs_sa11x0_poke(EP0_CONTROL, bits_to_set, bits_to_set, 0);
+}
+
+// Another utility function to empty the fifo. This involves similar
+// hardware problems to writing, it is possible to read a byte without
+// changing the fifo state so that next time the same byte would be
+// read again. Again there is a possible race condition if another
+// control message arrives while emptying the fifo.
+static int
+usbs_sa11x0_ep0_empty_fifo(unsigned char* buf)
+{
+ int count = *EP0_WRITE_COUNT & 0x00FF;
+ int emptied = 0;
+ cyg_bool ok = true;
+
+ CYG_ASSERT( (count >= 0) & (count <= 8), "EP0 write count must be in range");
+
+ while (ok && (emptied < count)) {
+ int retries, checks;
+ cyg_bool read = false;
+
+ for (retries = 0; !read && (retries < MAX_RETRIES); retries++) {
+ if ((count - emptied) != *EP0_WRITE_COUNT) {
+ DBG(("EP0_empty_fifo, inconsistency, read %d bytes of %d, but fifo count %d\n", emptied, count, *EP0_WRITE_COUNT));
+ ok = false;
+ } else {
+ buf[emptied] = *EP0_DATA;
+ for (checks = 0; !read && (checks < MAX_CHECKS); checks++) {
+ if ((count - emptied) > *EP0_WRITE_COUNT) {
+ //DBG(("Read %d byte (%x) after %d checks, %d retries\n", emptied, buf[emptied], checks, retries));
+ read = true;
+ emptied++;
+ }
+ }
+ }
+ }
+ if (ok && !read) {
+ DBG(("EP0 empty fifo, failed to read byte from fifo\n"));
+ ok = false;
+ }
+ }
+
+ return emptied;
+}
+
+// This is where all the hard work happens. It is a very large routine
+// for a DSR, but in practice nearly all of it is nested if's and very
+// little code actually gets executed. Note that there may be
+// invocations of callback functions and the driver has no control
+// over how much time those will take, but those callbacks should be
+// simple.
+static void
+usbs_sa11x0_ep0_dsr(void)
+{
+ int hw_state = *EP0_CONTROL;
+
+ // Handle the stall bits.
+ //
+ // Force-stall should not be a problem. It is set by the code here
+ // if the host needs to be told that the control message was
+ // unacceptable and is cleared automatically by the hardware after
+ // the stall is sent.
+ // NOTE: it is not clear the hardware actually works in this
+ // respect. The FORCE_STALL bit has been observed still set during
+ // the next interrupt, and the host appears to receive spurious
+ // data back in response to the next control packet.
+ //
+ // Sent-stall is set by the hardware following a protocol
+ // violation, e.g. if there is an IN token when a new control
+ // message is expected. There is nothing the software can do about
+ // this. However if we are in the middle of an IN or OUT transfer
+ // then those are not going to complete successfully.
+ if (0 != (hw_state & EP0_SENT_STALL)) {
+ if (EP0_STATE_IDLE != ep0.ep_state) {
+ if ((usbs_control_return (*)(usbs_control_endpoint*, int))0 != ep0.common.complete_fn) {
+ (*ep0.common.complete_fn)(&ep0.common, -EIO);
+ }
+ ep0.ep_state = EP0_STATE_IDLE;
+ ep0.common.buffer = (unsigned char*) 0;
+ ep0.common.buffer_size = 0;
+ ep0.common.fill_buffer_fn = 0;
+ ep0.common.complete_fn = (usbs_control_return (*)(usbs_control_endpoint*, int)) 0;
+ }
+ usbs_sa11x0_poke(EP0_CONTROL, EP0_SENT_STALL, 0, EP0_SENT_STALL);
+ } // STALL condition
+
+ // Next, check whether we have received a new control message
+ // while still busy processing an old one.
+ if (0 != (hw_state & EP0_SETUP_END)) {
+ if (EP0_STATE_IDLE != ep0.ep_state) {
+ if ((usbs_control_return (*)(usbs_control_endpoint*, int)) 0 != ep0.common.complete_fn) {
+ (*ep0.common.complete_fn)(&ep0.common, -EIO);
+ }
+ ep0.ep_state = EP0_STATE_IDLE;
+ ep0.common.buffer = (unsigned char*) 0;
+ ep0.common.buffer_size = 0;
+ ep0.common.fill_buffer_fn = 0;
+ ep0.common.complete_fn = (usbs_control_return (*)(usbs_control_endpoint*, int)) 0;
+ }
+ // We are now back in idle state so the control message will be
+ // extracted and processed.
+ usbs_sa11x0_poke(EP0_CONTROL, EP0_SERVICED_SETUP_END, 0, EP0_SETUP_END);
+ } // Interrupted control transaction
+
+ // The endpoint can be in one of three states: IN, OUT, or IDLE.
+ // For the first two it should mean that there is more data to be
+ // transferred, which is pretty straightforward. IDLE means
+ // that a new control message has arrived.
+ if ((EP0_STATE_IN == ep0.ep_state) && (0 == (EP0_IN_READY & hw_state))) {
+
+ usbs_sa11x0_ep0_fill_fifo();
+
+ } else if ((EP0_STATE_OUT == ep0.ep_state) && (0 != (EP0_OUT_READY & hw_state))) {
+
+ // A host->device transfer. Higher level code must have
+ // provided a suitable buffer.
+ CYG_ASSERT( (unsigned char*)0 != ep0.common.buffer, "A receive buffer should have been provided" );
+
+ ep0.transmitted += usbs_sa11x0_ep0_empty_fifo(ep0.common.buffer + ep0.transmitted);
+
+ if (ep0.transmitted != ep0.length) {
+ // The host is not allowed to send more data than it
+ // indicated in the original control message, and all
+ // messages until the last one should be full size.
+ CYG_ASSERT( ep0.transmitted < ep0.length, "The host must not send more data than expected");
+ CYG_ASSERT( 0 == (ep0.transmitted % EP0_FIFO_SIZE), "All OUT packets until the last one should be full-size");
+
+ usbs_sa11x0_poke(EP0_CONTROL, EP0_SERVICED_OPR, 0, EP0_OUT_READY);
+ } else {
+ // The whole transfer is now complete. Invoke the
+ // completion function, and based on its return value
+ // either generate a stall or complete the message.
+ usbs_control_return result;
+
+ CYG_ASSERT( (usbs_control_return (*)(usbs_control_endpoint*, int))0 != ep0.common.complete_fn, \
+ "A completion function should be provided for OUT control messages");
+
+ result = (*ep0.common.complete_fn)(&ep0.common, 0);
+ ep0.common.buffer = (unsigned char*) 0;
+ ep0.common.buffer_size = 0;
+ ep0.common.complete_fn = (usbs_control_return (*)(usbs_control_endpoint*, int)) 0;
+
+ if (USBS_CONTROL_RETURN_HANDLED == result) {
+ usbs_sa11x0_poke(EP0_CONTROL,
+ EP0_SERVICED_OPR | EP0_DATA_END,
+ EP0_DATA_END,
+ EP0_OUT_READY);
+ } else {
+ usbs_sa11x0_poke(EP0_CONTROL,
+ EP0_SERVICED_OPR | EP0_DATA_END | EP0_FORCE_STALL,
+ EP0_FORCE_STALL,
+ EP0_OUT_READY);
+ }
+ // Also remember to switch back to IDLE state
+ ep0.ep_state = EP0_STATE_IDLE;
+ }
+
+ } else if (0 != (EP0_OUT_READY & hw_state)) {
+
+ int emptied = usbs_sa11x0_ep0_empty_fifo(ep0.common.control_buffer);
+
+ if (8 != emptied) {
+ // This indicates a serious problem somewhere. Respond by
+ // stalling. Hopefully the host will take some action that
+ // sorts out the mess.
+ usbs_sa11x0_poke(EP0_CONTROL,
+ EP0_SERVICED_OPR | EP0_DATA_END | EP0_FORCE_STALL,
+ EP0_FORCE_STALL,
+ EP0_OUT_READY);
+
+ } else {
+ usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
+ usb_devreq* req = (usb_devreq*) ep0.common.control_buffer;
+ int length, direction, protocol, recipient;
+
+ // Now we need to do some decoding of the data. A non-zero
+ // length field indicates that there will be a subsequent
+ // IN or OUT phase. The direction is controlled by the
+ // top bit of the first byte. The protocol is determined
+ // by other bits of the top byte.
+ length = (req->length_hi << 8) | req->length_lo;
+ direction = req->type & USB_DEVREQ_DIRECTION_MASK;
+ protocol = req->type & USB_DEVREQ_TYPE_MASK;
+ recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
+
+#if 0
+ DBG(("ep0, new control request: type %x, code %x\n", req->type, req->request));
+ DBG((" %s, length %d, value hi %x lo %x, index hi %x lo %x\n",
+ (USB_DEVREQ_DIRECTION_OUT == direction) ? "out" : "in",
+ length, req->value_hi, req->value_lo, req->index_hi, req->index_lo));
+#endif
+ if (0 != length){
+ // Clear the fifo straightaway. There is no harm in
+ // doing this here. It may or may not do some good.
+ usbs_sa11x0_poke(EP0_CONTROL, EP0_SERVICED_OPR, 0, EP0_OUT_READY);
+ }
+
+ if (USB_DEVREQ_TYPE_STANDARD == protocol) {
+
+ // First see if the request can be handled entirely in
+ // this module.
+ if (USB_DEVREQ_SET_ADDRESS == req->request) {
+ // The USB device address should be in value_lo.
+ // No more data is expected.
+ int address = req->value_lo;
+ if ((0 != length) || (address > 127)) {
+ result = USBS_CONTROL_RETURN_STALL;
+ } else {
+ // poke_value() cannot be used here because
+ // setting the address does not take effect
+ // until the status phase.
+ *USBS_ADDRESS = address;
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+ } else if (USB_DEVREQ_GET_STATUS == req->request) {
+ // GET_STATUS on the device as a whole is used to
+ // check the remote-wakeup and self-powered bits.
+ // GET_STATUS on an endpoint is used to determine
+ // the halted condition.
+ // GET_STATUS on anything else has to be left to
+ // other code.
+ if (USB_DEVREQ_RECIPIENT_DEVICE == recipient) {
+ // The host should expect two bytes back.
+ if ((2 == length) && (USB_DEVREQ_DIRECTION_IN == direction)) {
+ ep0.common.control_buffer[0] = 0; // Not self-powered, no remote wakeup
+ ep0.common.control_buffer[1] = 0;
+ ep0.common.buffer = ep0.common.control_buffer;
+ ep0.common.buffer_size = 2;
+ result = USBS_CONTROL_RETURN_HANDLED;
+ } else {
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+
+ } else if (USB_DEVREQ_RECIPIENT_ENDPOINT == recipient) {
+ if ((2 == length) && (USB_DEVREQ_DIRECTION_IN == direction)) {
+ int endpoint = req->index_lo;
+ if (0 == endpoint) {
+ // get-status on endpoint 0 is either undefined or always valid.
+ // endpoint 0 is always up.
+ ep0.common.control_buffer[0] = 0;
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP1
+ else if (((USB_DEVREQ_INDEX_DIRECTION_OUT | 1) == endpoint) &&
+ (USBS_STATE_CONFIGURED == (ep0.common.state & USBS_STATE_MASK))) {
+
+ ep0.common.control_buffer[0] = ep1.common.halted;
+ result = USBS_CONTROL_RETURN_HANDLED;
+
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP2
+ else if (((USB_DEVREQ_INDEX_DIRECTION_IN | 2) == endpoint) &&
+ (USBS_STATE_CONFIGURED == (ep0.common.state & USBS_STATE_MASK))) {
+
+ ep0.common.control_buffer[0] = ep2.common.halted;
+ result = USBS_CONTROL_RETURN_HANDLED;
+
+ }
+#endif
+ else {
+ // An invalid endpoint has been specified or the
+ // endpoint can only be examined in configured state.
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+ if (USBS_CONTROL_RETURN_HANDLED == result) {
+ ep0.common.control_buffer[1] = 0;
+ ep0.common.buffer = ep0.common.control_buffer;
+ ep0.common.buffer_size = 2;
+ }
+ } else {
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+ } // Endpoint or device get-status
+
+ } else if (USB_DEVREQ_CLEAR_FEATURE == req->request) {
+
+ // CLEAR_FEATURE operates in much the same way as
+ // GET_STATUS
+ if (USB_DEVREQ_RECIPIENT_DEVICE == recipient) {
+
+ // No data should be transferred, and only remote-wakeup can be cleared.
+ if ((0 != length) || (USB_DEVREQ_FEATURE_DEVICE_REMOTE_WAKEUP != req->value_lo)) {
+ result = USBS_CONTROL_RETURN_STALL;
+ } else {
+ // Clearing remote-wakeup is a no-op.
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+
+ } else if (USB_DEVREQ_RECIPIENT_ENDPOINT == recipient) {
+ // The only feature that can be cleared is endpoint-halt, no data should be transferred.
+ if ((0 != length) || (USB_DEVREQ_FEATURE_ENDPOINT_HALT != req->value_lo)) {
+ result = USBS_CONTROL_RETURN_STALL;
+ } else {
+ int endpoint = req->index_lo;
+ if (0 == endpoint) {
+ // Clearing halt on endpoint 0 is always a no-op since that endpoint cannot be halted
+ }
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP1
+ else if (((USB_DEVREQ_INDEX_DIRECTION_OUT | 1) == endpoint) &&
+ (USBS_STATE_CONFIGURED == (ep0.common.state & USBS_STATE_MASK))) {
+ ep1_set_halted(&ep1.common, false);
+ result = USBS_CONTROL_RETURN_HANDLED;
+
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP2
+ else if (((USB_DEVREQ_INDEX_DIRECTION_IN | 2) == endpoint) &&
+ (USBS_STATE_CONFIGURED == (ep0.common.state & USBS_STATE_MASK))) {
+ ep2_set_halted(&ep2.common, false);
+ result = USBS_CONTROL_RETURN_HANDLED;
+
+ }
+#endif
+ else {
+ // Invalid endpoint or not in configured state.
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+ }
+ } // Endpoing or device clear-feature
+
+ } else if (USB_DEVREQ_SET_FEATURE == req->request) {
+
+ // SET_FEATURE also operates in much the same way as
+ // GET_STATUS
+ if (USB_DEVREQ_RECIPIENT_DEVICE == recipient) {
+
+ // The only valid feature that can be set is remote-wakeup,
+ // which is not supported by the hardware.
+ result = USBS_CONTROL_RETURN_STALL;
+
+ } else if (USB_DEVREQ_RECIPIENT_ENDPOINT == recipient) {
+
+ // Only the halt condition can be set, and no data should be transferred.
+ // Halting endpoint 0 should probably be disallowed although the
+ // standard does not explicitly say so.
+ if ((0 != length) ||
+ (USB_DEVREQ_FEATURE_ENDPOINT_HALT != req->value_lo) ||
+ (USBS_STATE_CONFIGURED != (ep0.common.state & USBS_STATE_MASK))) {
+
+ result = USBS_CONTROL_RETURN_STALL;
+
+ } else {
+ int endpoint = req->index_lo;
+ if (0) {
+ }
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP1
+ else if ((USB_DEVREQ_INDEX_DIRECTION_OUT | 1) == endpoint) {
+ ep1_set_halted(&ep1.common, true);
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP2
+ else if ((USB_DEVREQ_INDEX_DIRECTION_IN | 2) == endpoint) {
+ ep2_set_halted(&ep2.common, true);
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+#endif
+ else {
+ result = USBS_CONTROL_RETURN_STALL;
+ }
+ }
+ } // Endpoint or device set-feature
+ }
+
+ // If the result has not been handled yet, pass it to
+ // the installed callback function (if any).
+ if (USBS_CONTROL_RETURN_UNKNOWN == result) {
+ if ((usbs_control_return (*)(usbs_control_endpoint*, void*))0 != ep0.common.standard_control_fn) {
+ result = (*ep0.common.standard_control_fn)(&ep0.common, ep0.common.standard_control_data);
+ }
+ }
+
+#if 1
+ if ((USBS_CONTROL_RETURN_UNKNOWN == result) &&
+ (USB_DEVREQ_SET_INTERFACE == req->request)) {
+
+ // This code should not be necessary. For
+ // non-trivial applications which involve
+ // alternate interfaces and the like, this request
+ // should be handled by the application itself.
+ // For other applications, the default handler
+ // will ignore this request so we end up falling
+ // through without actually handling the request
+ // and hence returning a stall condition. That
+ // is legitimate behaviour according to the standard.
+ //
+ // However, there are appear to be problems with
+ // the SA1110 USB hardware when it comes to stall
+ // conditions: they appear to affect some
+ // subsequent messages from target to host as
+ // well. Hence rather than returning a stall
+ // condition this code instead generates a dummy
+ // reply, which is also valid according to the
+ // standard. This avoids complications with certain
+ // USB compliance testers.
+ if ((0 != length) ||
+ (0 != req->value_hi) || (0 != req->index_hi) ||
+ (USBS_STATE_CONFIGURED != (ep0.common.state & USBS_STATE_MASK))) {
+
+ result = USBS_CONTROL_RETURN_STALL;
+ } else {
+ int interface_id;
+ int alternate;
+
+ CYG_ASSERT( (1 == ep0.common.enumeration_data->device.number_configurations) && \
+ (1 == ep0.common.enumeration_data->total_number_interfaces), \
+ "Higher level code should have handled this request");
+
+ interface_id = req->index_lo;
+ alternate = req->value_lo;
+ if ((interface_id != ep0.common.enumeration_data->interfaces[0].interface_id) ||
+ (alternate != ep0.common.enumeration_data->interfaces[0].alternate_setting)) {
+
+ result = USBS_CONTROL_RETURN_STALL;
+ } else {
+ result = USBS_CONTROL_RETURN_HANDLED;
+ }
+
+ }
+ }
+#endif
+
+ // If the result has still not been handled, leave it to
+ // the default implementation in the USB slave common package
+ if (USBS_CONTROL_RETURN_UNKNOWN == result) {
+ result = usbs_handle_standard_control(&ep0.common);
+ }
+
+ } else {
+ // The other three types of control message can be
+ // handled by similar code.
+ usbs_control_return (*callback_fn)(usbs_control_endpoint*, void*);
+ void* callback_arg;
+ //DBG(("non-standard control request %x", req->request));
+
+ if (USB_DEVREQ_TYPE_CLASS == protocol) {
+ callback_fn = ep0.common.class_control_fn;
+ callback_arg = ep0.common.class_control_data;
+ } else if (USB_DEVREQ_TYPE_VENDOR == protocol) {
+ callback_fn = ep0.common.vendor_control_fn;
+ callback_arg = ep0.common.vendor_control_data;
+ } else {
+ callback_fn = ep0.common.reserved_control_fn;
+ callback_arg = ep0.common.reserved_control_data;
+ }
+
+ if ((usbs_control_return (*)(usbs_control_endpoint*, void*)) 0 == callback_fn) {
+ result = USBS_CONTROL_RETURN_STALL;
+ } else {
+ result = (*callback_fn)(&ep0.common, callback_arg);
+ }
+ }
+ //DBG(("Control request done, %d\n", result));
+
+ if (USBS_CONTROL_RETURN_HANDLED != result) {
+ // This control request cannot be handled. Generate a stall.
+ usbs_sa11x0_poke(EP0_CONTROL,
+ EP0_FORCE_STALL | EP0_SERVICED_OPR | EP0_DATA_END,
+ EP0_FORCE_STALL,
+ EP0_OUT_READY);
+ } else {
+ // The control request has been handled. Is there any more
+ // data to be transferred?
+ if (0 == length) {
+ usbs_sa11x0_poke(EP0_CONTROL,
+ EP0_SERVICED_OPR | EP0_DATA_END,
+ EP0_DATA_END,
+ EP0_OUT_READY);
+ } else {
+ // The endpoint should now go into IN or OUT mode while the
+ // remaining data is transferred.
+ ep0.transmitted = 0;
+ ep0.length = length;
+ if (USB_DEVREQ_DIRECTION_OUT == direction) {
+ // Wait for the next packet from the host.
+ ep0.ep_state = EP0_STATE_OUT;
+ CYG_ASSERT( (unsigned char*) 0 != ep0.common.buffer, "A receive buffer should have been provided");
+ CYG_ASSERT( (usbs_control_return (*)(usbs_control_endpoint*, int))0 != ep0.common.complete_fn, \
+ "A completion function should be provided for OUT control messages");
+ } else {
+ ep0.ep_state = EP0_STATE_IN;
+ usbs_sa11x0_ep0_fill_fifo();
+ }
+ }
+ } // Control message handled
+ } // Received 8-byte control message
+ } // Idle state, i.e. control message
+} // ep0_dsr
+
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP1
+// ----------------------------------------------------------------------------
+// Endpoint 1 is used for OUT transfers, i.e. receive operations. Only
+// the bulk protocol is supported by the hardware. The semantics allow
+// for two different modes of operation: higher-level code can ask for
+// exactly one or more bulk packets of 64 bytes each, allowing buffer
+// requirements to be determined from a header; alternatively the
+// rx request can just supply a large buffer. Processing the first
+// packet of a larger transfer separately does not introduce any
+// special problems at the protocol level.
+//
+// It is not legal to receive just part of a packet and expect the
+// hardware or the driver to buffer the rest. Not all hardware will
+// be capable of doing this buffering, and there should not be
+// a driver design requirement to provide buffering space.
+//
+//
+// The hardware design for endpoint 1 is flawed in a number of
+// respects. The receive fifo is only 20 bytes, less than the packet
+// size, so it is essential to use DMA (there is a configuration
+// option to allow for communication protocols where packets will
+// never exceed 16 bytes, but that is not the normal case). The DMA
+// engine is triggered by a receive-fifo-service high-water mark
+// bit. DMA transfers operate in bursts of eight bytes. Therefore
+// it would make sense if the high-water mark was set when the
+// receive fifo contained eight bytes or more.
+//
+// Instead the high-water mark is set when the fifo contains twelve
+// bytes or more. Worse, there is no way of measuring how many bytes
+// there are left in the fifo without actually extracting those bytes.
+//
+// For a full-size 64-byte packet, the first 56 bytes will be
+// transferred by DMA and the remainder will remain in the fifo. For a
+// partial packet of between 56 and 63 bytes, the first 56 bytes will
+// be transferred by DMA and the remainder will remain in the fifo. There
+// is no way to distinguish between these scenarios without emptying
+// the fifo.
+//
+// The result is that there is never any point in attempting a DMA
+// transfer of more than 56 bytes, and for every endpoint 1 interrupt
+// it is necessary to read the remainder from the fifo. This adds
+// a lot of software overhead, and it is not clear that DMA is
+// particularly useful. It is still necessary because of the limited
+// fifo size.
+//
+//
+// Because DMA involves the use of physical rather than virtual
+// memory, there are also cache interaction problems. Specifically it
+// would be necessary to invalidate cache lines after a DMA transfer
+// has completed, but that only works sensibly if the buffer is
+// aligned to a cache-line boundary and is a multiple of the
+// cache-line size. Imposing such restrictions on higher-level code
+// is undesirable. Also the DMA engines have an apparently undocumented
+// restriction that the buffer must be eight-byte aligned.
+//
+// To work around all these problems, the receive code works in terms
+// of a small private buffer. After a packet has been received, data
+// will be copied from this private buffer to the destination. Obviously
+// this copy operation is overhead and, because the code is expected
+// to run at DSR level, However the copy operation is limited to at
+// most 64 bytes, which is not good but not disastrous either.
+//
+// For data transfers the entry points are:
+//
+// 1) ep1_start_rx_packet() - prepare to receive another packet from
+// the host.
+// 2) ep1_clear_error() - an error condition has occurred (CRC,
+// bit-stuffing, fifo overrun). It appears that the only way
+// to clear this is to clear the receive-packet-complete bit,
+// which unfortunately allows in another packet from the host
+// before we are ready for it. Doing anything else while
+// the error bit is set does not work, for example it is not
+// possible to empty the fifo by hand.
+// 3) ep1_process_packet() - a whole packet has been received
+// and now needs to be moved into application space.
+//
+// These three routines are called by the start_rx() routine and
+// by the DSR. There are different implementations for DMA and
+// non-DMA.
+//
+// There is another hardware problem: the receive-packet-complete bit
+// comes up with the wrong default value, allowing the host to start
+// transmitting before the target is ready to receive. Unfortunately
+// there is not much that can be done about this: the
+// receive-packet-complete bit cannot be set by software and the OUT
+// max register has a minimum size of eight bytes. Fortunately for
+// many protocols the target-side code has a chance to start a receive
+// before the host is allowed to send, so this problem is mostly
+// ignored for now.
+//
+// Another potential problem arises if the host sends more data than
+// is expected for a given transfer. It would be possible to address
+// this by manipulating the OUT max packet register and getting the
+// hardware to generate protocol violation stalls. This would also
+// eliminate the need to test for buffer overflows. For now it is
+// left to higher-level code to sort it all out.
+
+#ifdef CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL
+
+// DMA needs an area of physical memory. To avoid conflicts with
+// the cached shadow of this memory, this area needs to start at
+// a cache line boundary and there must be padding at the end
+// to the next cache line boundary, thus ensuring that the
+// processor will not accidentally overwrite the physical
+// memory because it is manipulating some other variable.
+//
+// NOTE: at the time of writing the toolchain has a problem with
+// the aligned attribute, so instead the start alignment has
+// to be handled in software.
+
+# define EP1_DMA_MTU 56
+# define EP1_DMA_BUFSIZE ((EP1_DMA_MTU + HAL_DCACHE_LINE_SIZE - 1) - \
+ ((EP1_DMA_MTU + HAL_DCACHE_LINE_SIZE - 1) % HAL_DCACHE_LINE_SIZE))
+# define EP1_DMA_ALLOCSIZE (EP1_DMA_BUFSIZE + HAL_DCACHE_LINE_SIZE - 1)
+
+static unsigned char ep1_dma_data[EP1_DMA_ALLOCSIZE];
+
+// This variable cannot be initialized statically, instead it is
+// set by ep1_init(). It corresponds to the physical address
+// for the buffer.
+static unsigned char* ep1_dma_buf;
+
+static void
+ep1_start_rx_packet(void)
+{
+ int dma_size = EP1_DMA_MTU;
+
+ // This assertion does not always hold: clearing an error condition
+ // involves the packet-complete bit so another message may have
+ // started to arrive.
+ // CYG_ASSERT( 0 == (EP1_FIFO_NOT_EMPTY & *EP1_CONTROL), "The receive fifo should be empty");
+
+ CYG_ASSERT( 0 == ((DMA_CONTROL_RUN | DMA_CONTROL_START_A) & *EP1_DMA_STATUS), "EP1 DMA should be inactive");
+
+#ifdef FAILURES
+ ep1_failure = (ep1_failure + 1) % 32;
+ if (0 == ep1_failure) {
+ dma_size = 8;
+ }
+#endif
+
+ // The full flexibility of the DMA engines is not required here,
+ // specifically the automatic chaining between buffers A and B.
+ // Instead always using buffer A is sufficient. To avoid the
+ // However the hardware still requires the software to alternate
+ // between A and B. To avoid switching between buffers during a
+ // transfer an excessive size field is used, EP1_MTU rather than
+ // EP1_DMA_MTU, and hence the DMA transfer will never complete.
+ //
+ // With some silicon revisions writing to the DMA registers does
+ // not always work either, so a retry is in order. Possibly
+ // some short delays immediately after the clear and before the
+ // set would be sufficient.
+ *EP1_DMA_CONTROL_CLEAR = DMA_CONTROL_CLEAR_ALL;
+ if (0 == (DMA_STATUS_BUFFER_IN_USE & *EP1_DMA_STATUS)) {
+ ep1.using_buf_a = true;
+ usbs_sa11x0_poke_value(EP1_DMA_BUF_A_ADDRESS, (unsigned int) ep1_dma_buf);
+ usbs_sa11x0_poke_value(EP1_DMA_BUF_A_SIZE, dma_size);
+ *EP1_DMA_CONTROL_SET = DMA_CONTROL_RUN | DMA_CONTROL_START_A;
+ } else {
+ ep1.using_buf_a = false;
+ usbs_sa11x0_poke_value(EP1_DMA_BUF_B_ADDRESS, (unsigned int) ep1_dma_buf);
+ usbs_sa11x0_poke_value(EP1_DMA_BUF_B_SIZE, dma_size);
+ *EP1_DMA_CONTROL_SET = DMA_CONTROL_RUN | DMA_CONTROL_START_B;
+ }
+
+ // This should not be necessary, but occasionally the equivalent
+ // operation during ep1_init() fails. Strictly speaking it should
+ // be calling poke_value(), but the added overheads for that are
+ // not worthwhile.
+ *USBS_OUT_SIZE = EP1_MTU - 1;
+
+ // Now allow the host to send the packet.
+ usbs_sa11x0_poke(EP1_CONTROL, EP1_PACKET_COMPLETE | EP1_SENT_STALL, 0,
+ EP1_PACKET_COMPLETE | EP1_SENT_STALL | EP1_FORCE_STALL);
+}
+
+// Clear an error condition following a CRC, bit stuffing or overrun
+// error. The only reliable way to do this is to halt DMA and clear
+// the packet-complete bit. Unfortunately this allows the host to send
+// another packet immediately, before start_rx_packet can be called,
+// introducing another race condition. The hardware does not appear
+// to offer any alternatives.
+static void
+ep1_clear_error(void)
+{
+ *EP1_DMA_CONTROL_CLEAR = DMA_CONTROL_CLEAR_ALL;
+ usbs_sa11x0_poke(EP1_CONTROL, EP1_PACKET_COMPLETE | EP1_SENT_STALL, 0,
+ EP1_PACKET_COMPLETE | EP1_PACKET_ERROR | EP1_SENT_STALL | EP1_FORCE_STALL | EP1_FIFO_NOT_EMPTY);
+
+ // Clearing the packet-complete bit may cause the host to send
+ // another packet, immediately causing another error, so this
+ // assertion does not hold.
+ // CYG_ASSERT( 0 == (*EP1_CONTROL & (EP1_PACKET_ERROR | EP1_FIFO_NOT_EMPTY)), "Receive error should have been cleared");
+}
+
+// A packet has been received. Some of it may still be in the fifo
+// and must be extracted by hand. The data then has to copied to
+// a higher-level buffer.
+static int
+ep1_process_packet(void)
+{
+ int pkt_size;
+
+ // First, work out how much data has been processed by the DMA
+ // engine. This is the amount originally poked into the size
+ // register minus its current value.
+ *EP1_DMA_CONTROL_CLEAR = DMA_CONTROL_CLEAR_ALL;
+ if (ep1.using_buf_a) {
+ pkt_size = EP1_DMA_MTU - *EP1_DMA_BUF_A_SIZE;
+ } else {
+ pkt_size = EP1_DMA_MTU - *EP1_DMA_BUF_B_SIZE;
+ }
+ CYG_ASSERT( 0 == (pkt_size % DMA_BURST_SIZE), "DMA transfers must be in multiples of the burst size");
+
+ // Move these bytes from physical memory to the target buffer.
+ if ((pkt_size > 0) && ((ep1.fetched + pkt_size) < ep1.common.buffer_size)) {
+ memcpy(ep1.common.buffer + ep1.fetched, ep1_dma_buf, pkt_size);
+ }
+
+ // Copy remaining bytes into the target buffer directly.
+ // The DMA buffer could be used instead, moving the memcpy()
+ // down and avoiding the need for a buffer overflow check
+ // inside the loop, but at the cost of accessing physical
+ // memory every time. That cost is too high.
+ while (1) {
+ int status = *EP1_CONTROL;
+ if ((EP1_PACKET_COMPLETE | EP1_PACKET_ERROR) == ((EP1_PACKET_COMPLETE | EP1_PACKET_ERROR) & status)) {
+ break;
+ } else if (0 == (EP1_FIFO_NOT_EMPTY & status)) {
+ break;
+ } else {
+ int datum = *EP1_DATA;
+ if (ep1.fetched < ep1.common.buffer_size) {
+ ep1.common.buffer[ep1.fetched + pkt_size] = datum;
+ }
+ pkt_size++;
+ }
+ }
+ ep1.fetched += pkt_size;
+ return pkt_size;
+}
+
+#else
+
+// Transfers not involving DMA. Obviously these are much simpler
+// but restricted to packets of 16 bytes.
+static void
+ep1_start_rx_packet(void)
+{
+ // Nothing to be done, just let the host send a packet and it will
+ // end up in the fifo.
+ usbs_sa11x0_poke(EP1_CONTROL, EP1_PACKET_COMPLETE | EP1_SENT_STALL, 0,
+ EP1_PACKET_COMPLETE | EP1_SENT_STALL | EP1_FORCE_STALL);
+}
+
+static void
+ep1_clear_error(void)
+{
+ usbs_sa11x0_poke(EP1_CONTROL, EP1_PACKET_COMPLETE | EP1_SENT_STALL, 0,
+ EP1_PACKET_COMPLETE | EP1_SENT_STALL | EP1_FORCE_STALL);
+}
+
+static int
+ep1_process_packet(void)
+{
+ int pkt_size = 0;
+ while (0 != (*EP1_CONTROL & EP1_FIFO_NOT_EMPTY)) {
+ int datum = *EP1_DATA;
+ pkt_size++;
+ if (ep1.fetched < ep1.common.buffer_size) {
+ ep1.common.buffer[ep1.fetched + pkt_size] = datum;
+ }
+ }
+ return pkt_size;
+}
+#endif
+
+// Complete a transfer. This takes care of invoking the completion
+// callback and resetting the buffer.
+static void
+ep1_rx_complete(int result)
+{
+ void (*complete_fn)(void*, int) = ep1.common.complete_fn;
+ void* complete_data = ep1.common.complete_data;
+
+ ep1.common.buffer = (unsigned char*) 0;
+ ep1.common.buffer_size = 0;
+ ep1.common.complete_fn = (void (*)(void*, int)) 0;
+ ep1.common.complete_data = (void*) 0;
+
+ if ((void (*)(void*, int))0 != complete_fn) {
+ (*complete_fn)(complete_data, result);
+ }
+}
+
+// Start a transmission. This functionality is overloaded to cope with
+// waiting for stalls to complete.
+static void
+ep1_start_rx(usbs_rx_endpoint* endpoint)
+{
+ CYG_ASSERT( endpoint == &ep1.common, "USB data transfer involves the wrong endpoint");
+
+ // Is this endpoint currently stalled? If so then a size of 0 can
+ // be used to block until the stall condition is clear, anything
+ // else should result in an immediate callback.
+ if (ep1.common.halted) {
+ if (0 != ep1.common.buffer_size) {
+ ep1_rx_complete(-EAGAIN);
+ }
+ } else if (0 == ep1.common.buffer_size) {
+ // A check to see if the endpoint is halted. It isn't.
+ ep1_rx_complete(0);
+ } else {
+ int status = *EP1_CONTROL;
+
+ CYG_ASSERT((void*) 0 != ep1.common.buffer, "USB receives should not override the interrupt vectors");
+
+ // This indicates the start of a transfer.
+ ep1.fetched = 0;
+
+ // The sent-stall bit may get set by hardware because of
+ // a protocol violation. If so it must be cleared here.
+ if (0 != (status & EP1_SENT_STALL)) {
+ usbs_sa11x0_poke(EP1_CONTROL, EP1_SENT_STALL, 0, EP1_SENT_STALL | EP1_FORCE_STALL);
+ status = *EP1_CONTROL;
+ }
+
+ // The bogus initial value for the receive-packet-complete
+ // bit means that we may start off with an error condition.
+ if ((EP1_PACKET_COMPLETE | EP1_PACKET_ERROR) == (status & (EP1_PACKET_COMPLETE | EP1_PACKET_ERROR))) {
+ ep1_clear_error();
+ ep1_start_rx_packet();
+ } else if (0 != (status & EP1_FIFO_NOT_EMPTY)) {
+ // No error but data in the fifo. This implies a small
+ // initial packet, all held in the fifo.
+#ifdef CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL
+ *EP1_DMA_BUF_A_SIZE = EP1_MTU;
+ ep1.using_buf_a = true;
+#endif
+ (void) ep1_process_packet();
+ ep1_rx_complete(ep1.fetched);
+ } else {
+ // Start a new transfer.
+ ep1_start_rx_packet();
+ }
+ }
+}
+
+static void
+ep1_set_halted(usbs_rx_endpoint* endpoint, cyg_bool new_value)
+{
+ CYG_ASSERT( endpoint == &ep1.common, "USB set-stall operation involves the wrong endpoint");
+
+ if (ep1.common.halted == new_value) {
+ return;
+ }
+ if (new_value) {
+ // The endpoint should be stalled. There is a potential race
+ // condition here with a current transfer. Updating the
+ // stalled flag means that the dsr will do nothing.
+ ep1.common.halted = true;
+ HAL_REORDER_BARRIER();
+
+ // Now perform the actual stall. If we are in the middle of a
+ // transfer then the stall bit may not get set for a while, so
+ // poke() is inappropriate.
+ *EP1_CONTROL = EP1_FORCE_STALL;
+ } else {
+ // The stall condition should be cleared. First take care of
+ // things at the hardware level so that a new transfer is
+ // allowed.
+ usbs_sa11x0_poke(EP1_CONTROL, EP1_SENT_STALL, 0, EP1_SENT_STALL | EP1_FORCE_STALL);
+
+ // Now allow new transfers to begin.
+ ep1.common.halted = false;
+ }
+}
+
+// The DSR is invoked following an interrupt. According to the docs an
+// endpoint 1 interrupt can only happen if the receive-packet-complete
+// bit is set.
+static void
+usbs_sa11x0_ep1_dsr(void)
+{
+ int status = *EP1_CONTROL;
+
+ // This assertion does not always hold. During long runs
+ // spurious interrupts have been observed.
+ // CYG_ASSERT( 0 != (status & EP1_PACKET_COMPLETE), "ep1 dsr should only be invoked when there is data");
+ if (0 == (status & EP1_PACKET_COMPLETE)) {
+ return;
+ }
+
+ if (ep1.common.halted) {
+ // Do nothing. What may have happened is that a transfer
+ // was in progress when the stall bit was set. The
+ // set_halted() call above will have taken care of things.
+ return;
+ }
+
+ // The sent-stall bit should never get set, since we always
+ // accept full-size 64-byte packets. Just in case...
+ if (0 != (status & EP1_SENT_STALL)) {
+ DBG(("ep1_dsr(), sent-stall bit\n"));
+ usbs_sa11x0_poke(EP1_CONTROL, EP1_SENT_STALL, 0, EP1_SENT_STALL | EP1_FORCE_STALL);
+ }
+
+ // Was there a receive error (CRC, bit-stuffing, fifo-overrun?).
+ // Whichever bits of the current packet have been received must be
+ // discarded, and the current packet must be retried.
+ if (0 != (status & EP1_PACKET_ERROR)) {
+ INCR_STAT(ep1_errors);
+ ep1_clear_error();
+ ep1_start_rx_packet();
+ } else {
+ // Another packet has been received. Process it, which may
+ // complete the transfer or it may leave more to be done.
+ //
+ // The hardware starts with the wrong default value for
+ // the receive-packet-complete bit, so a packet may arrive
+ // even though no rx operation has started yet. The
+ // packets must be ignored for now. start_rx_packet()
+ // will detect data in the fifo and do the right thing.
+ int pkt_size;
+
+ if ((unsigned char*)0 != ep1.common.buffer) {
+
+ pkt_size = ep1_process_packet();
+ INCR_STAT(ep1_receives);
+ if (0 != (EP1_PACKET_ERROR & *EP1_CONTROL)) {
+ CYG_ASSERT( 0, "an error has occurred inside ep1_process_packet()\n");
+
+ } else if ((ep1.fetched != ep1.common.buffer_size) && (0 != pkt_size) && (0 == (ep1.fetched % EP1_MTU))) {
+ ep1_start_rx_packet();
+ } else if (ep1.fetched > ep1.common.buffer_size) {
+ // The host has sent too much data.
+ ep1_rx_complete(-EMSGSIZE);
+ } else {
+#if 0
+ int i;
+ diag_printf("------------------------------------------------------\n");
+ diag_printf("rx: buf %x, total size %d\n", ep1.common.buffer, ep1.fetched);
+ for (i = 0; (i < ep1.fetched) && (i < 128); i+= 8) {
+ diag_printf("rx %x %x %x %x %x %x %x %x\n",
+ ep1.common.buffer[i+0], ep1.common.buffer[i+1], ep1.common.buffer[i+2], ep1.common.buffer[i+3],
+ ep1.common.buffer[i+4], ep1.common.buffer[i+5], ep1.common.buffer[i+6], ep1.common.buffer[i+7]);
+ }
+ diag_printf("------------------------------------------------------\n");
+#endif
+ ep1_rx_complete(ep1.fetched);
+ }
+ }
+ }
+}
+
+// Initialization.
+//
+// This may get called during system start-up or following a reset
+// from the host.
+static void
+usbs_sa11x0_ep1_init(void)
+{
+#ifdef CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL
+ // What is the physical address that should be used for
+ // transfers?
+ unsigned int phys;
+ HAL_VIRT_TO_PHYS_ADDRESS( ep1_dma_data, phys);
+ phys += (HAL_DCACHE_LINE_SIZE - 1);
+ phys -= (phys % HAL_DCACHE_LINE_SIZE);
+ CYG_ASSERT( 0 == (phys % HAL_DCACHE_LINE_SIZE), "DMA buffer must be aligned to a cache-line boundary");
+ ep1_dma_buf = (unsigned char*)phys;
+
+ // Clear the DMA channel and fix the DMA address register. The
+ // value is determined above.
+ *EP1_DMA_CONTROL_CLEAR = DMA_CONTROL_CLEAR_ALL;
+ *EP1_DMA_ADDRESS = EP1_DMA_ADDRESS_VALUE;
+#endif
+
+ // Always allow the host to send full-size packets. If there is a
+ // protocol problem and the host sends packets that are too large,
+ // it will have to be handled at a level above the device driver.
+ //
+ // With some silicon revisions reading back the register does not
+ // work, so poke_value() is not applicable. This may be an issue
+ // with reset timing.
+ *USBS_OUT_SIZE = EP1_MTU - 1;
+
+ // Endpoints should never be halted during a start-up.
+ ep1.common.halted = false;
+
+ // If there has been a reset and there was a receive in progress,
+ // abort it. This also takes care of sorting out the endpoint
+ // fields ready for the next rx.
+ ep1_rx_complete(-EPIPE);
+}
+
+#endif // CYGPKG_DEVS_USB_SA11X0_EP1
+
+
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP2
+// ----------------------------------------------------------------------------
+// Endpoint 2 is used for IN transfers, i.e. transmitting data to the
+// host. The code is mostly similar to that for endpoint 1, although
+// a little bit simpler (e.g. there is no need to worry about
+// buffer overflow, that is the host's problem).
+//
+// There is a flaw in the hardware design. If the transfer involves an
+// exact multiple of 64 bytes then according to the USB spec there
+// should be a terminating packet of 0 bytes. However the size of the
+// current outgoing packet is determined by the IN_SIZE register and
+// that only allows for packets between 1 and 256 bytes - even though
+// USB bulk transfers can only go up to 64 bytes. This can be worked
+// around at this level by transmitting an extra byte, at the risk of
+// upsetting host-side device drivers. Both higher-level and host-side
+// code need to be aware of this problem.
+//
+// Again there appear to be problems with the DMA engine. This time it
+// appears that the transmit-fifo-service bit does not always work
+// correctly. If you set up a DMA transfer for more than the packet
+// size than once the packet has gone out the fifo-service bit just
+// remains set, the DMA engine continues to fill the fifo, and the
+// data gets lost. Instead DMA can only happen one packet at a time.
+// The same issues regarding cache line alignment etc. arise, so
+// using a small buffer here is convenient.
+//
+// 1) process_packet moves a packet from the main transmit buffer
+// into the dma buffer.
+// 2) start_tx_packet() starts a transfer to the host
+// 3) clear_error() copes with error conditions.
+
+#ifdef CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL
+// See the equivalent EP1 DMA definitions.
+# define EP2_DMA_MTU 64
+# define EP2_DMA_BUFSIZE ((EP2_DMA_MTU + HAL_DCACHE_LINE_SIZE - 1) - \
+ ((EP2_DMA_MTU + HAL_DCACHE_LINE_SIZE - 1) % HAL_DCACHE_LINE_SIZE))
+# define EP2_DMA_ALLOCSIZE (EP2_DMA_BUFSIZE + HAL_DCACHE_LINE_SIZE - 1)
+
+static unsigned char ep2_dma_data[EP2_DMA_ALLOCSIZE];
+static unsigned char* ep2_dma_buf;
+
+static void
+ep2_process_packet(void)
+{
+ ep2.pkt_size = ep2.common.buffer_size - ep2.transmitted;
+ if (ep2.pkt_size > EP2_MTU) {
+ ep2.pkt_size = EP2_MTU;
+ }
+ // Work around the hardware's inability to send a zero-byte packet.
+ if (0 == ep2.pkt_size) {
+ ep2.pkt_size = 1;
+ ep2_dma_buf[0] = 0;
+ } else {
+ memcpy(ep2_dma_buf, ep2.common.buffer + ep2.transmitted, ep2.pkt_size);
+ }
+}
+
+static void
+ep2_tx_packet(void)
+{
+ int dma_size, dma_control_settings;
+
+ // CYG_ASSERT( 0 != (*EP2_CONTROL & EP2_FIFO_SERVICE), "Fifo should be empty");
+
+ // Halt any DMA that may still be going on (there should not
+ // be any). Then work out the desired DMA settings for the
+ // current packet. The DMA engine needs to transfer a multiple
+ // of the burst size. If the packet size is not a multiple of
+ // the burst size, this presents a minor problem. The chances
+ // of an interrupt handler running in time to put the
+ // remaining bytes into the fifo by hand are not good, so
+ // instead more data is DMA'd in then is absolutely necessary
+ // and the surplus bytes will be cleared out during the next
+ // tx_packet.
+ //
+ // A possible optimisation is to detect small packets of
+ // less than the fifo size and byte-stuff those, bypassing
+ // DMA. It is not clear that would give any performance benefits.
+ *EP2_DMA_CONTROL_CLEAR = DMA_CONTROL_CLEAR_ALL;
+
+ dma_size = ep2.pkt_size + DMA_BURST_SIZE - 1;
+ dma_size -= (dma_size % DMA_BURST_SIZE);
+
+ CYG_ASSERT(dma_size > 0, "DMA calculations should result in a transfer of at least 8 bytes");
+
+ // Now clear the fifo, after DMA has stopped.
+ usbs_sa11x0_poke(EP2_CONTROL, EP2_SENT_STALL, 0, EP2_SENT_STALL);
+
+ // Should we be using buf_a or buf_b for this transfer?
+ // Getting this wrong means that the DMA engine just idles.
+ if (0 == (*EP2_DMA_STATUS & DMA_STATUS_BUFFER_IN_USE)) {
+ usbs_sa11x0_poke_value(EP2_DMA_BUF_A_ADDRESS, (int) ep2_dma_buf);
+ usbs_sa11x0_poke_value(EP2_DMA_BUF_A_SIZE, dma_size);
+ dma_control_settings = DMA_CONTROL_RUN | DMA_CONTROL_START_A;
+ } else {
+ usbs_sa11x0_poke_value(EP2_DMA_BUF_B_ADDRESS, (int) ep2_dma_buf);
+ usbs_sa11x0_poke_value(EP2_DMA_BUF_B_SIZE, dma_size);
+ dma_control_settings = DMA_CONTROL_RUN | DMA_CONTROL_START_B;
+ }
+
+ // Poke the tx size register while the fifo is clearing.
+ // This operation must be reliable or the host will get
+ // confused by funny-sized packets.
+ usbs_sa11x0_poke_value(USBS_IN_SIZE, ep2.pkt_size - 1);
+
+ // The USB hardware must be updated before the DMA engine
+ // starts filling the fifo. Otherwise ~48% of outgoing
+ // packets fail with a DMA underrun. When called from
+ // start_tx() there is a race condition: if the host
+ // request comes in before the DMA starts then an
+ // error interrupt will be raised, to be processed by
+ // the DSR, and then the DMA engine gets updated again.
+ // Locking the scheduler eliminates this race.
+ cyg_drv_dsr_lock();
+ usbs_sa11x0_poke(EP2_CONTROL, EP2_PACKET_COMPLETE, 0, EP2_PACKET_COMPLETE | EP2_PACKET_ERROR | EP2_PACKET_UNDERRUN);
+ *EP2_DMA_CONTROL_SET = dma_control_settings;
+ cyg_drv_dsr_unlock();
+
+ // CYG_ASSERT(0 == (*EP2_CONTROL & EP2_FIFO_SERVICE), "DMA engine should have filled up the fifo by now");
+}
+
+// Clearing an error should be a no-op when DMA is involved.
+// In practice clearing the packet-complete bit appears to
+// have some desirable effects, at the risk of the host
+// getting bogus data. This should only happen when there
+// is a real transfer in progress: an error early on is
+// likely because the PACKET_COMPLETE bit has a bogus initial
+// value.
+static void
+ep2_clear_error(void)
+{
+ usbs_sa11x0_poke(EP2_CONTROL, EP2_PACKET_COMPLETE, 0, EP2_PACKET_COMPLETE | EP2_PACKET_ERROR | EP2_PACKET_UNDERRUN);
+}
+
+#else // EP2_DMA
+
+// When not using DMA, process_packet() is responsible for filling the
+// fifo and keeping a shadow copy in a static buffer. clear_error()
+// refills the fifo using the shadow copy. tx_packet() starts the
+// actual transfer.
+static unsigned char ep2_tx_buf[EP2_MTU];
+
+static void
+ep2_process_packet()
+{
+ int i;
+
+ // Clear the fifo, just in case.
+ usbs_sa11x0_poke(EP2_CONTROL, EP2_SENT_STALL, 0, EP2_SENT_STALL);
+
+ ep2.pkt_size = ep2.common.buffer_size - ep2.transmitted;
+ if (ep2.pkt_size > EP2_MTU) {
+ ep2.pkt_size = EP2_MTU;
+ }
+ if (0 == ep2.pkt_size) {
+ ep2.pkt_size = 1;
+ ep2_tx_buf[i] = 0;
+ *EP2_DATA = 0;
+ } else {
+ for (i = 0; i < ep2.pkt_size; i++) {
+ unsigned int datum = ep2.common.buffer[ep2.transmitted + i];
+ ep2_tx_buf[i] = datum;
+ *EP2_DATA = datum;
+ }
+ }
+}
+
+static void
+ep2_tx_packet()
+{
+ usbs_sa11x0_poke_value(USBS_IN_SIZE, ep2.pkt_size - 1);
+ usbs_sa11x0_poke(EP2_CONTROL, EP2_PACKET_COMPLETE, 0, EP2_PACKET_COMPLETE | EP2_PACKET_ERROR | EP2_PACKET_UNDERRUN);
+}
+
+static void
+ep2_clear_error()
+{
+ int i;
+ // Clear the fifo, just in case.
+ usbs_sa11x0_poke(EP2_CONTROL, EP2_SENT_STALL, 0, EP2_SENT_STALL);
+ for (i = 0; i < ep2.pkt_size; i++) {
+ *EP2_DATA = ep2_tx_buf[i];
+ }
+}
+
+#endif // !EP2_DMA
+
+// A utility routine for completing a transfer. This takes care of the
+// callback as well as resetting the buffer.
+static void
+ep2_tx_complete(int result)
+{
+ void (*complete_fn)(void*, int) = ep2.common.complete_fn;
+ void* complete_data = ep2.common.complete_data;
+
+ ep2.common.buffer = (unsigned char*) 0;
+ ep2.common.buffer_size = 0;
+ ep2.common.complete_fn = (void (*)(void*, int)) 0;
+ ep2.common.complete_data = (void*) 0;
+
+ if ((void (*)(void*, int))0 != complete_fn) {
+ (*complete_fn)(complete_data, result);
+ }
+}
+
+
+// The exported interface to start a transmission.
+static void
+ep2_start_tx(usbs_tx_endpoint* endpoint)
+{
+ CYG_ASSERT( endpoint == &ep2.common, "USB data transfer involves the wrong endpoint");
+
+ // Is this endpoint currently stalled? If so then a size of 0 can
+ // be used to block until the stall condition is clear, anything
+ // else should result in an immediate callback.
+ if (ep2.common.halted) {
+ if (0 != ep2.common.buffer_size) {
+ ep2_tx_complete(-EAGAIN);
+ }
+ } else if (0 == ep2.common.buffer_size) {
+ // A check to see if the endpoint is halted. It isn't.
+ ep2_tx_complete(0);
+ } else {
+ // There should not be any errors at the start of a
+ // transmission, but if there is one then there is no safe way
+ // to recover. process_packet() and tx_packet() will hopefully
+ // do the right thing.
+ CYG_ASSERT((void*) 0 != ep2.common.buffer, "Transmitting the interrupt vectors is unlikely to be useful");
+#if 0
+ {
+ int i;
+ diag_printf("----------------------------------------------\n");
+ diag_printf("ep2_start_tx: buf %x, %d bytes\n", ep2.common.buffer, ep2.common.buffer_size);
+ for (i = 0; (i < ep2.common.buffer_size) && (i < 128); i+= 8) {
+ diag_printf("tx: %x %x %x %x %x %x %x %x\n",
+ ep2.common.buffer[i+0], ep2.common.buffer[i+1], ep2.common.buffer[i+2], ep2.common.buffer[i+3],
+ ep2.common.buffer[i+4], ep2.common.buffer[i+5], ep2.common.buffer[i+6], ep2.common.buffer[i+7]);
+ }
+ diag_printf("----------------------------------------------\n");
+ }
+#endif
+
+ // Prepare the first packet for transmission, then send it.
+ ep2.transmitted = 0;
+ ep2_process_packet();
+ ep2_tx_packet();
+ }
+}
+
+static void
+ep2_set_halted(usbs_tx_endpoint* endpoint, cyg_bool new_value)
+{
+ CYG_ASSERT(endpoint == &ep2.common, "USB set-stall operation involves the wrong endpoint");
+
+ if (ep2.common.halted == new_value) {
+ return;
+ }
+ if (new_value) {
+ // The endpoint should be stalled. There is a potential race
+ // condition here with the current transfer and DSR invocation.
+ // Updating the stalled flag means that the DSR will do nothing.
+ ep2.common.halted = true;
+ HAL_REORDER_BARRIER();
+
+ // Now perform the actual stall. This may be delayed by the hardware
+ // so poke() cannot be used.
+ *EP2_CONTROL = EP2_FORCE_STALL;
+
+ // If in the middle of a transfer then that cannot be aborted,
+ // the DMA engines etc. would get very confused.
+ } else {
+ // Take care of the hardware so that a new transfer is allowed.
+ usbs_sa11x0_poke(EP2_CONTROL, EP2_SENT_STALL, 0, EP2_SENT_STALL | EP2_FORCE_STALL);
+ ep2.common.halted = false;
+ }
+}
+
+// The dsr will be invoked when the transmit-packet-complete bit is
+// set. Typically this happens when a packet has been completed
+// (surprise surprise) but it can also happen for error conditions.
+static void
+usbs_sa11x0_ep2_dsr(void)
+{
+ int status = *EP2_CONTROL;
+ // This assertion does not always hold - spurious interrupts have
+ // been observed if you run for a few hours.
+ // CYG_ASSERT( 0 != (status & EP2_PACKET_COMPLETE), "ep2 dsr should only be invoked when the packet-complete bit is set");
+
+ if (0 == (status & EP2_PACKET_COMPLETE)) {
+ // Spurious interrupt, do nothing.
+ } else if (ep2.common.halted) {
+ // There is a possible race condition between a packet
+ // completing and the stalled condition being set.
+ // set_halted() above does everything that is needed.
+ } else if (0 == ep2.pkt_size) {
+ // This can happen because of the initial value for the
+ // packet-complete bit, allowing the host to retrieve data
+ // before the target is ready. The correct action is to do
+ // nothing.
+ } else if (0 != (status & (EP2_PACKET_ERROR | EP2_PACKET_UNDERRUN))) {
+ // A transmit error occurred, the details are not important.
+ INCR_STAT(ep2_errors);
+ ep2_clear_error();
+ ep2_tx_packet();
+ } else {
+ // Another packet has gone out.
+ INCR_STAT(ep2_transmits);
+ ep2.transmitted += ep2.pkt_size;
+ if ((ep2.transmitted < ep2.common.buffer_size) ||
+ ((ep2.transmitted == ep2.common.buffer_size) && (0 == (ep2.common.buffer_size % EP2_MTU)))) {
+ ep2_process_packet();
+ ep2_tx_packet();
+ } else {
+ ep2_tx_complete(ep2.transmitted);
+ }
+ }
+}
+
+// Endpoint 2 initialization.
+//
+// This may be called during system start-up or following a reset
+// from the host.
+static void
+usbs_sa11x0_ep2_init(void)
+{
+#ifdef CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL
+ // What is the physical address that should be used for
+ // transfers?
+ unsigned int phys;
+ HAL_VIRT_TO_PHYS_ADDRESS(ep2_dma_data, phys);
+ phys += (HAL_DCACHE_LINE_SIZE - 1);
+ phys -= (phys % HAL_DCACHE_LINE_SIZE);
+ CYG_ASSERT(0 == (phys % HAL_DCACHE_LINE_SIZE), "DMA buffer must be aligned to a cache-line boundary");
+ ep2_dma_buf = (unsigned char*) phys;
+
+ // Clear the DMA channel completely, otherwise it may not be
+ // possible to write the ADDRESS register. Then set the DMA
+ // address register. The value is determined above.
+ *EP2_DMA_CONTROL_CLEAR = DMA_CONTROL_CLEAR_ALL;
+ *EP2_DMA_ADDRESS = EP2_DMA_ADDRESS_VALUE;
+#endif
+
+ // Endpoints should never be halted after a reset
+ ep2.common.halted = false;
+
+ // If there has been a reset and there was a receive in progress,
+ // abort it. This also takes care of clearing the endpoint
+ // structure fields.
+ ep2_tx_complete(-EPIPE);
+}
+
+#endif // CYGPKG_DEVS_USB_SA11X0_EP2
+
+// ----------------------------------------------------------------------------
+// Interrupt handling
+//
+// As much work as possible is deferred to the DSR (or to the debug
+// thread). Interrupts for the endpoints are never a problem: the
+// variuos packet-complete etc. bits ensure that the endpoints
+// remain quiescent until the relevant interrupt has been serviced.
+// Suspend and resume are more complicated. A suspend means that
+// there has been no activity for 3ms, which should be enough
+// time for the whole thing to be handled. A resume means that there
+// has been bus activity after a suspend, and again it is infrequent.
+//
+// Reset appears to be much more complicated. A reset means that the
+// host is holding the USB lines to a specific state for 10ms. This is
+// detected by the hardware, causing the USB controller to be reset
+// (i.e. any pending transfers are discarded, etc.). The reset bit in
+// the status register will be set, and an interrupt will be raised.
+// Now, in theory the correct thing to do is to process this
+// interrupt, block reset interrupts for the duration of these 10ms,
+// and wait for further activity such as the control message to set
+// the address.
+//
+// In practice this does not seem to work. Possibly the USB controller
+// gets reset continuously while the external reset signal is applied,
+// but I have not been able to confirm this. Messing about with the
+// reset interrupt control bit causes the system to go off into
+// never-never land. 10ms is too short a time to allow for manual
+// debugging of what happens. So for now the interrupt source is
+// blocked at the interrupt mask level and the dsr will do the
+// right thing. This causes a significant number of spurious interrupts
+// for the duration of the reset signal and not a lot else can happen.
+
+
+// Perform reset operations on all endpoints that have been
+// configured in. It is convenient to keep this in a separate
+// routine to allow for polling, where manipulating the
+// interrupt controller mask is a bad idea.
+static void
+usbs_sa11x0_handle_reset(void)
+{
+ int old_state = ep0.common.state;
+
+ // Any state change must be reported to higher-level code
+ ep0.common.state = USBS_STATE_DEFAULT;
+ if ((void (*)(usbs_control_endpoint*, void*, usbs_state_change, int))0 != ep0.common.state_change_fn) {
+ (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+ USBS_STATE_CHANGE_RESET, old_state);
+ }
+
+ // Reinitialize all the endpoints that have been configured in.
+ usbs_sa11x0_ep0_init();
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP1
+ usbs_sa11x0_ep1_init();
+#endif
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP2
+ usbs_sa11x0_ep2_init();
+#endif
+
+ // After a reset we need to handle endpoint interrupts, reset
+ // interrupts, and suspend interrupts. There should not be a
+ // resume since we have not suspended, but leaving resume
+ // interrupts enabled appears to be desirable with some hardware.
+ //
+ // With some silicon revisions it appears that a longer delay
+ // is needed after reset, so this poke() may not work.
+ if (!usbs_sa11x0_poke(USBS_CONTROL,
+ CONTROL_INTR_ENABLE(CONTROL_EP_INTR_BITS|CONTROL_RESET_INTR|CONTROL_SUSPEND_INTR|CONTROL_RESUME_INTR),
+ 0,
+ CONTROL_INTR_CLEAR(CONTROL_EP_INTR_BITS|CONTROL_RESET_INTR|CONTROL_SUSPEND_INTR|CONTROL_RESUME_INTR))) {
+ // DBG(("usbs_sa11x0_handle_reset(), update of control register failed, status %x\n", *USBS_STATUS));
+ }
+}
+
+// The DSR. This can be invoked directly by poll(), or via the usual
+// interrupt subsystem. It acts as per the current value of
+// isr_status_bits. If another interrupt goes off while this
+// DSR is running, there will be another invocation of the DSR and
+// the status bits will be updated.
+static void
+usbs_sa11x0_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+ int status = 0;
+
+ CYG_ASSERT(SA11X0_IRQ_USB_SERVICE_REQUEST == vector, "USB DSR should only be invoked for USB interrupts" );
+ CYG_ASSERT(0 == data, "The SA11X0 USB DSR needs no global data pointer");
+
+ // There is no atomic swap support, so interrupts have to be
+ // blocked. It might be possible to do this via the USBS_CONTROL
+ // register, but at the risk of messing up the status register
+ // if another interrupt comes in. Blocking interrupts at the
+ // processor level is less intrusive on the USB code.
+
+ cyg_drv_isr_lock();
+ status = isr_status_bits;
+ isr_status_bits = 0;
+ cyg_drv_isr_unlock();
+
+ // Reset is special, since it invalidates everything else.
+ // If the reset is still ongoing then do not attempt any
+ // further processing, there will just be another interrupt.
+ // Otherwise handle_reset() does the hard work. Unmasking
+ // the interrupt means that another interrupt will occur
+ // immediately if reset is still asserted, i.e. no threads
+ // will run, but there is no easy way of triggering action
+ // at the end of reset.
+ if (0 != (status & STATUS_RESET_INTR)) {
+
+ int new_status = *USBS_STATUS;
+ if (0 == (new_status & STATUS_RESET_INTR)) {
+ usbs_sa11x0_handle_reset();
+ }
+ // This unmask is likely to cause another interrupt immediately
+ cyg_drv_interrupt_unmask(SA11X0_IRQ_USB_SERVICE_REQUEST);
+
+ } else {
+ // Process resume first. Ignore any resumes when we are not
+ // actually suspended yet, this happens mainly during system
+ // startup. If there has been a state change to suspended
+ // then we need a matching state change to resumed.
+ if (0 != (status & STATUS_RESUME_INTR)) {
+ int old_state = ep0.common.state;
+ if (0 != (old_state & USBS_STATE_SUSPENDED)) {
+ ep0.common.state &= ~USBS_STATE_SUSPENDED;
+ if ((void (*)(usbs_control_endpoint*, void*, usbs_state_change, int))0 != ep0.common.state_change_fn) {
+ (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+ USBS_STATE_CHANGE_RESUMED, old_state);
+ }
+ // After a resume, all interrupts should be enabled.
+ // In theory there is no need to worry about further
+ // resume interrupts, but strange hardware behaviour
+ // has been observed if resume interrupts are left
+ // disabled.
+ usbs_sa11x0_poke(USBS_CONTROL,
+ CONTROL_INTR_ENABLE(CONTROL_EP_INTR_BITS|CONTROL_RESET_INTR|CONTROL_SUSPEND_INTR|CONTROL_RESUME_INTR),
+ 0,
+ CONTROL_INTR_CLEAR(CONTROL_EP_INTR_BITS|CONTROL_RESET_INTR|CONTROL_SUSPEND_INTR|CONTROL_RESUME_INTR));
+ }
+ }
+
+ // Now process endpoint interrupts. Control operations on
+ // endpoint 0 may have side effects on the other endpoints
+ // so it is better to leave them until last.
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP1
+ if (0 != (status & STATUS_EP1_INTR)) {
+ usbs_sa11x0_ep1_dsr();
+ }
+#endif
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP2
+ if (0 != (status & STATUS_EP2_INTR)) {
+ usbs_sa11x0_ep2_dsr();
+ }
+#endif
+ if (0 != (status & STATUS_EP0_INTR)) {
+ usbs_sa11x0_ep0_dsr();
+ }
+
+ // Process suspend last, but only if there has not also been
+ // a resume. A suspend immediately followed by a resume should
+ // be ignored. A resume immediately followed by a suspend
+ // would be unfortunate, but suspend means that there has been
+ // at least 3ms of inactivity so the DSR latency would have
+ // to be pretty bad.
+ //
+ // Total robustness is possible but requires more work in the ISR.
+ if ((0 != (status & STATUS_SUSPEND_INTR)) && (0 == (status & STATUS_RESUME_INTR))) {
+ int old_state = ep0.common.state;
+ ep0.common.state |= USBS_STATE_SUSPENDED;
+ if ((void (*)(usbs_control_endpoint*, void*, usbs_state_change, int))0 != ep0.common.state_change_fn) {
+ (*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
+ USBS_STATE_CHANGE_SUSPENDED, old_state);
+ }
+ // We are no longer interested in further suspend interrupts,
+ // which could happen every 3 ms, but resume has become
+ // very interesting.
+ usbs_sa11x0_poke(USBS_CONTROL,
+ CONTROL_INTR_ENABLE(CONTROL_EP_INTR_BITS | CONTROL_RESET_INTR | CONTROL_RESUME_INTR),
+ 0,
+ CONTROL_INTR_CLEAR(CONTROL_EP_INTR_BITS | CONTROL_RESET_INTR | CONTROL_RESUME_INTR));
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Optionally the USB code can do most of its processing in a thread
+// rather than in a DSR.
+#ifdef CYGPKG_DEVS_USB_SA11X0_THREAD
+static unsigned char usbs_sa11x0_thread_stack[CYGNUM_DEVS_USB_SA11X0_THREAD_STACK_SIZE];
+static cyg_thread usbs_sa11x0_thread;
+static cyg_handle_t usbs_sa11x0_thread_handle;
+static cyg_sem_t usbs_sa11x0_sem;
+
+
+static void
+usbs_sa11x0_thread_fn(cyg_addrword_t param)
+{
+ for (;;) {
+ cyg_semaphore_wait(&usbs_sa11x0_sem);
+ usbs_sa11x0_dsr(SA11X0_IRQ_USB_SERVICE_REQUEST, 0, 0);
+ }
+ CYG_UNUSED_PARAM(cyg_addrword_t, param);
+}
+
+static void
+usbs_sa11x0_thread_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+ CYG_ASSERT( 0 != isr_status_bits, "DSR's should only be scheduled when there is work to do");
+ cyg_semaphore_post(&usbs_sa11x0_sem);
+
+ CYG_UNUSED_PARAM(cyg_vector_t, vector);
+ CYG_UNUSED_PARAM(cyg_ucount32, count);
+ CYG_UNUSED_PARAM(cyg_addrword_t, data);
+}
+
+#endif
+
+// ----------------------------------------------------------------------------
+// The interrupt handler. This does as little as possible.
+static cyg_uint32
+usbs_sa11x0_isr(cyg_vector_t vector, cyg_addrword_t data)
+{
+ int old_status_bits = isr_status_bits;
+ int status_bits;
+
+ CYG_ASSERT(SA11X0_IRQ_USB_SERVICE_REQUEST == vector, "USB ISR should only be invoked for USB interrupts" );
+ CYG_ASSERT(0 == data, "The SA11X0 USB ISR needs no global data pointer" );
+
+ // Read the current status. Reset is special, it means that the
+ // whole chip has been reset apart from the one bit in the status
+ // register. Nothing should be done about this until the DSR sets
+ // the endpoints back to a consistent state and re-enables
+ // interrupts in the control register.
+ status_bits = *USBS_STATUS;
+
+ if (0 != (status_bits & STATUS_RESET_INTR)) {
+ isr_status_bits = STATUS_RESET_INTR;
+ *USBS_STATUS = status_bits;
+ cyg_drv_interrupt_mask(SA11X0_IRQ_USB_SERVICE_REQUEST);
+ } else {
+ *USBS_STATUS = status_bits;
+ isr_status_bits |= status_bits;
+ }
+
+ // Now keep the rest of the system happy.
+ cyg_drv_interrupt_acknowledge(vector);
+ return (old_status_bits != isr_status_bits) ? CYG_ISR_CALL_DSR : CYG_ISR_HANDLED;
+}
+
+// ----------------------------------------------------------------------------
+// Polling support. This acts mostly like the interrupt handler: it
+// sets the isr status bits and causes the dsr to run. Reset has to be
+// handled specially: polling does nothing as long as reset is asserted.
+
+static void
+usbs_sa11x0_poll(usbs_control_endpoint* endpoint)
+{
+ CYG_ASSERT( endpoint == &ep0.common, "USB poll involves the wrong endpoint");
+
+ if (0 != (isr_status_bits & STATUS_RESET_INTR)) {
+ // Reset was detected the last time poll() was invoked. If
+ // reset is still active, do nothing. Once the reset has
+ // completed things can continue.
+ if (0 == (STATUS_RESET_INTR & *USBS_STATUS)) {
+ isr_status_bits = 0;
+ usbs_sa11x0_handle_reset();
+ }
+ } else {
+ isr_status_bits = *USBS_STATUS;
+ if (0 != (STATUS_RESET_INTR & isr_status_bits)) {
+ // Reset has just been asserted. Do nothing, just continue
+ // polling for the duration of the reset signal.
+ } else if (0 != isr_status_bits) {
+ usbs_sa11x0_dsr(SA11X0_IRQ_USB_SERVICE_REQUEST, 0, (cyg_addrword_t) 0);
+ }
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+// Initialization.
+
+void
+usbs_sa11x0_init(void)
+{
+ // Start by disabling/resetting the hardware. This is easy.
+ *USBS_CONTROL = CONTROL_DISABLE;
+ *USBS_CONTROL = CONTROL_DISABLE;
+ *USBS_CONTROL = CONTROL_DISABLE;
+
+ // The USB bus is now tristated, preventing any communications.
+ // This is a good thing, the situation should change only when
+ // higher-level code has provided the enumeration data and done an
+ // explicit start.
+ usbs_sa11x0_ep0_init();
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP1
+ usbs_sa11x0_ep1_init();
+#endif
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP2
+ usbs_sa11x0_ep2_init();
+#endif
+
+ // If processing is supposed to happen in a thread rather
+ // than in DSR, initialize the threads.
+#ifdef CYGPKG_DEVS_USB_SA11X0_THREAD
+ cyg_semaphore_init(&usbs_sa11x0_sem, 0);
+ cyg_thread_create(CYGNUM_DEVS_USB_SA11X0_THREAD_PRIORITY,
+ &usbs_sa11x0_thread_fn,
+ 0,
+ "SA11X0 USB support",
+ usbs_sa11x0_thread_stack,
+ CYGNUM_DEVS_USB_SA11X0_THREAD_STACK_SIZE,
+ &usbs_sa11x0_thread_handle,
+ &usbs_sa11x0_thread
+ );
+ cyg_thread_resume(usbs_sa11x0_thread_handle);
+#endif
+
+ // It is also possible and desirable to install the interrupt
+ // handler here, even though there will be no interrupts for a
+ // while yet.
+ cyg_drv_interrupt_create(SA11X0_IRQ_USB_SERVICE_REQUEST,
+ 99, // priority
+ 0, // data
+ &usbs_sa11x0_isr,
+#ifdef CYGPKG_DEVS_USB_SA11X0_THREAD
+ &usbs_sa11x0_thread_dsr,
+#else
+ &usbs_sa11x0_dsr,
+#endif
+ &usbs_sa11x0_intr_handle,
+ &usbs_sa11x0_intr_data);
+ cyg_drv_interrupt_attach(usbs_sa11x0_intr_handle);
+ cyg_drv_interrupt_unmask(SA11X0_IRQ_USB_SERVICE_REQUEST);
+}
+
+// ----------------------------------------------------------------------------
+// Testing support.
+usbs_testing_endpoint usbs_testing_endpoints[] = {
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_CONTROL,
+ endpoint_number : 0,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &ep0.common,
+#ifdef CYGVAR_DEVS_USB_SA11X0_EP0_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_SA11X0_DEVTAB_BASENAME "0c",
+#else
+ devtab_entry : (const char*) 0,
+#endif
+ min_size : 1, // zero-byte control transfers are meaningless
+ max_size : 0x0FFFF, // limit imposed by protocol
+ max_in_padding : 0,
+ alignment : 0
+ },
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP1
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+ endpoint_number : 1,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_OUT,
+ endpoint : (void*) &ep1.common,
+#ifdef CYGVAR_DEVS_USB_SA11X0_EP1_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_SA11X0_DEVTAB_BASENAME "1r",
+#else
+ devtab_entry : (const char*) 0,
+#endif
+ min_size : 1,
+ max_size : -1, // No hardware or driver limitation
+ max_in_padding : 0,
+ alignment : 0
+ },
+#endif
+#ifdef CYGPKG_DEVS_USB_SA11X0_EP2
+ {
+ endpoint_type : USB_ENDPOINT_DESCRIPTOR_ATTR_BULK,
+ endpoint_number : 2,
+ endpoint_direction : USB_ENDPOINT_DESCRIPTOR_ENDPOINT_IN,
+ endpoint : (void*) &ep2.common,
+#ifdef CYGVAR_DEVS_USB_SA11X0_EP2_DEVTAB_ENTRY
+ devtab_entry : CYGDAT_DEVS_USB_SA11X0_DEVTAB_BASENAME "2w",
+#else
+ devtab_entry : (const char*) 0,
+#endif
+ min_size : 1,
+ max_size : -1, // No hardware or driver limitation
+ max_in_padding : 1, // hardware limitation
+ alignment : 0
+ },
+#endif
+ USBS_TESTING_ENDPOINTS_TERMINATOR
+};
diff --git a/ecos/packages/devs/usb/sa11x0/current/src/usbs_sa11x0_data.cxx b/ecos/packages/devs/usb/sa11x0/current/src/usbs_sa11x0_data.cxx
new file mode 100644
index 0000000..0ce3f3b
--- /dev/null
+++ b/ecos/packages/devs/usb/sa11x0/current/src/usbs_sa11x0_data.cxx
@@ -0,0 +1,173 @@
+//==========================================================================
+//
+// usbs_sa11x0.c
+//
+// Static data for the SA11x0 USB device driver
+//
+//==========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 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): bartv
+// Contributors: bartv
+// Date: 2000-10-04
+//
+// This file contains various objects that should go into extras.o
+// rather than libtarget.a, e.g. devtab entries that would normally
+// be eliminated by the selective linking.
+//
+//####DESCRIPTIONEND####
+//==========================================================================
+
+#include <cyg/io/devtab.h>
+#include <cyg/io/usb/usbs_sa11x0.h>
+#include <pkgconf/devs_usb_sa11x0.h>
+
+// ----------------------------------------------------------------------------
+// Initialization. The goal here is to call usbs_sa11x0_init()
+// early on during system startup, to take care of things like
+// registering interrupt handlers etc. which are best done
+// during system init.
+//
+// If the endpoint 0 devtab entry is available then its init()
+// function can be used to take care of this. However the devtab
+// entries are optional so an alternative mechanism must be
+// provided. Unfortunately although it is possible to give
+// a C function the constructor attribute, it cannot be given
+// an initpri attribute. Instead it is necessary to define a
+// dummy C++ class.
+
+extern "C" void usbs_sa11x0_init(void);
+
+#ifndef CYGVAR_DEVS_USB_SA11X0_EP0_DEVTAB_ENTRY
+class usbs_sa11x0_initialization {
+ public:
+ usbs_sa11x0_initialization() {
+ usbs_sa11x0_init();
+ }
+};
+
+static usbs_sa11x0_initialization usbs_sa11x0_init_object CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_IO);
+#endif
+
+// ----------------------------------------------------------------------------
+// The devtab entries. Each of these is optional, many applications
+// will want to use the lower-level API rather than go via
+// open/read/write/ioctl.
+
+#ifdef CYGVAR_DEVS_USB_SA11X0_EP0_DEVTAB_ENTRY
+
+// For endpoint 0 the only legal operations are get_config() and
+// set_config(), and these are provided by the common package.
+
+static bool
+usbs_sa11x0_devtab_ep0_init(struct cyg_devtab_entry* tab)
+{
+ CYG_UNUSED_PARAM(struct cyg_devtab_entry*, tab);
+ usbs_sa11x0_init();
+ return true;
+}
+
+static CHAR_DEVIO_TABLE(usbs_sa11x0_ep0_devtab_functions,
+ &cyg_devio_cwrite,
+ &cyg_devio_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+static CHAR_DEVTAB_ENTRY(usbs_sa11x0_ep0_devtab_entry,
+ CYGDAT_DEVS_USB_SA11X0_DEVTAB_BASENAME "0c",
+ 0,
+ &usbs_sa11x0_ep0_devtab_functions,
+ &usbs_sa11x0_devtab_ep0_init,
+ 0,
+ (void*) &usbs_sa11x0_ep0);
+#endif
+
+// ----------------------------------------------------------------------------
+// Common routines for ep1 and ep2.
+#if defined(CYGVAR_DEVS_USB_SA11X0_EP1_DEVTAB_ENTRY) || defined(CYGVAR_DEVS_USB_SA11X0_EP2_DEVTAB_ENTRY)
+static bool
+usbs_sa11x0_devtab_dummy_init(struct cyg_devtab_entry* tab)
+{
+ CYG_UNUSED_PARAM(struct cyg_devtab_entry*, tab);
+ return true;
+}
+#endif
+
+// ----------------------------------------------------------------------------
+// ep1 devtab entry. This can only be used for host->slave, so only the
+// cread() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_SA11X0_EP1_DEVTAB_ENTRY
+
+static CHAR_DEVIO_TABLE(usbs_sa11x0_ep1_devtab_functions,
+ &cyg_devio_cwrite,
+ &usbs_devtab_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+static CHAR_DEVTAB_ENTRY(usbs_sa11x0_ep1_devtab_entry,
+ CYGDAT_DEVS_USB_SA11X0_DEVTAB_BASENAME "1r",
+ 0,
+ &usbs_sa11x0_ep1_devtab_functions,
+ &usbs_sa11x0_devtab_dummy_init,
+ 0,
+ (void*) &usbs_sa11x0_ep1);
+#endif
+
+// ----------------------------------------------------------------------------
+// ep2 devtab entry. This can only be used for slave->host, so only
+// the cwrite() function makes sense.
+
+#ifdef CYGVAR_DEVS_USB_SA11X0_EP2_DEVTAB_ENTRY
+
+static CHAR_DEVIO_TABLE(usbs_sa11x0_ep2_devtab_functions,
+ &usbs_devtab_cwrite,
+ &cyg_devio_cread,
+ &cyg_devio_select,
+ &usbs_devtab_get_config,
+ &usbs_devtab_set_config);
+
+static DEVTAB_ENTRY(usbs_sa11x0_ep2_devtab_entry,
+ CYGDAT_DEVS_USB_SA11X0_DEVTAB_BASENAME "2w",
+ 0,
+ &usbs_sa11x0_ep2_devtab_functions,
+ &usbs_sa11x0_devtab_dummy_init,
+ 0,
+ (void*) &usbs_sa11x0_ep2);
+
+#endif
+