OpenCores
URL https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk

Subversion Repositories openrisc_me

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/rtos/ecos-2.0/packages/devs/usb
    from Rev 27 to Rev 174
    Reverse comparison

Rev 27 → Rev 174

/nec_upd985xx/v2_0/cdl/usbs_upd985xx.cdl
0,0 → 1,293
# ====================================================================
#
# 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) 2002 Bart Veer
## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
##
## eCos is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free
## Software Foundation; either version 2 or (at your option) any later version.
##
## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
## for more details.
##
## You should have received a copy of the GNU General Public License along
## with eCos; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
##
## As a special exception, if other files instantiate templates or use macros
## or inline functions from this file, or you compile this file and link it
## with other works to produce a work based on this file, this file does not
## by itself cause the resulting work to be covered by the GNU General Public
## License. However the source code for this file must still be made available
## in accordance with section (3) of the GNU General Public License.
##
## This exception does not invalidate any other reasons why a work based on
## this file might be covered by the GNU General Public License.
##
## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
## at http://sources.redhat.com/ecos/ecos-license/
## -------------------------------------------
#####ECOSGPLCOPYRIGHTEND####
# ====================================================================
######DESCRIPTIONBEGIN####
#
# Author(s): bartv
# 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."
}
}
/nec_upd985xx/v2_0/include/usbs_upd985xx.h
0,0 → 1,79
#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 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): bartv
// 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 */
/nec_upd985xx/v2_0/doc/devs-usb-nec-upd985xx.html
0,0 → 1,372
<!-- Copyright (C) 2001 Red Hat, 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
>
/nec_upd985xx/v2_0/doc/usbs_upd985xx.sgml
0,0 → 1,259
<!-- DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" -->
 
<!-- {{{ Banner -->
 
<!-- =============================================================== -->
<!-- -->
<!-- usbs_nec_upd9850x.sgml -->
<!-- -->
<!-- Documentation for the NEC uPD9850x USB Device Driver. -->
<!-- -->
<!-- =============================================================== -->
<!-- ####COPYRIGHTBEGIN#### -->
<!-- -->
<!-- =============================================================== -->
<!-- Copyright (C) 2001, 2002 Red Hat, 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 -->
<!-- =============================================================== -->
<!-- -->
<!-- ####COPYRIGHTEND#### -->
<!-- =============================================================== -->
<!-- #####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 -->
/nec_upd985xx/v2_0/doc/makefile
0,0 → 1,55
#=============================================================================
#
# 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 Red Hat, Inc.
##
## eCos is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free
## Software Foundation; either version 2 or (at your option) any later version.
##
## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
## for more details.
##
## You should have received a copy of the GNU General Public License along
## with eCos; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
##
## As a special exception, if other files instantiate templates or use macros
## or inline functions from this file, or you compile this file and link it
## with other works to produce a work based on this file, this file does not
## by itself cause the resulting work to be covered by the GNU General Public
## License. However the source code for this file must still be made available
## in accordance with section (3) of the GNU General Public License.
##
## This exception does not invalidate any other reasons why a work based on
## this file might be covered by the GNU General Public License.
##
## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
## at http://sources.redhat.com/ecos/ecos-license/
## -------------------------------------------
#####ECOSGPLCOPYRIGHTEND####
#=============================================================================
#####DESCRIPTIONBEGIN####
#
# Author(s): bartv
# Date: 2001-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
/nec_upd985xx/v2_0/src/usbs_upd985xx_data.cxx
0,0 → 1,190
//==========================================================================
//
// 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 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): bartv
// 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
 
/nec_upd985xx/v2_0/src/usbs_upd985xx.c
0,0 → 1,2689
//==========================================================================
//
// usbs_upd985xx.c
//
// Driver for the NEC uPD985xx USB device
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 2002 Bart Veer
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): bartv
// 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
};
/nec_upd985xx/v2_0/ChangeLog
0,0 → 1,136
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.
 
//===========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//===========================================================================
/sa11x0/v2_0/cdl/usbs_sa11x0.cdl
0,0 → 1,203
# ====================================================================
#
# 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 Red Hat, Inc.
##
## eCos is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free
## Software Foundation; either version 2 or (at your option) any later version.
##
## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
## for more details.
##
## You should have received a copy of the GNU General Public License along
## with eCos; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
##
## As a special exception, if other files instantiate templates or use macros
## or inline functions from this file, or you compile this file and link it
## with other works to produce a work based on this file, this file does not
## by itself cause the resulting work to be covered by the GNU General Public
## License. However the source code for this file must still be made available
## in accordance with section (3) of the GNU General Public License.
##
## This exception does not invalidate any other reasons why a work based on
## this file might be covered by the GNU General Public License.
##
## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
## at http://sources.redhat.com/ecos/ecos-license/
## -------------------------------------------
#####ECOSGPLCOPYRIGHTEND####
# ====================================================================
######DESCRIPTIONBEGIN####
#
# Author(s): bartv
# 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.
"
}
}
/sa11x0/v2_0/include/usbs_sa11x0.h
0,0 → 1,74
#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 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): bartv
// 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 */
/sa11x0/v2_0/doc/devs-usb-sa11x0.html
0,0 → 1,341
<!-- Copyright (C) 2001 Red Hat, 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>
/sa11x0/v2_0/doc/usbs_sa11x0.sgml
0,0 → 1,236
<!-- DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V3.1//EN" -->
 
<!-- {{{ Banner -->
 
<!-- =============================================================== -->
<!-- -->
<!-- usbs_sa11x0.sgml -->
<!-- -->
<!-- Documentation for the SA11x0 USB Device Driver. -->
<!-- -->
<!-- =============================================================== -->
<!-- ####COPYRIGHTBEGIN#### -->
<!-- -->
<!-- =============================================================== -->
<!-- Copyright (C) 2001, 2002 Red Hat, 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 -->
<!-- =============================================================== -->
<!-- -->
<!-- ####COPYRIGHTEND#### -->
<!-- =============================================================== -->
<!-- #####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 -->
/sa11x0/v2_0/doc/makefile
0,0 → 1,55
#=============================================================================
#
# 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 Red Hat, Inc.
##
## eCos is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free
## Software Foundation; either version 2 or (at your option) any later version.
##
## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
## for more details.
##
## You should have received a copy of the GNU General Public License along
## with eCos; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
##
## As a special exception, if other files instantiate templates or use macros
## or inline functions from this file, or you compile this file and link it
## with other works to produce a work based on this file, this file does not
## by itself cause the resulting work to be covered by the GNU General Public
## License. However the source code for this file must still be made available
## in accordance with section (3) of the GNU General Public License.
##
## This exception does not invalidate any other reasons why a work based on
## this file might be covered by the GNU General Public License.
##
## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
## at http://sources.redhat.com/ecos/ecos-license/
## -------------------------------------------
#####ECOSGPLCOPYRIGHTEND####
#=============================================================================
#####DESCRIPTIONBEGIN####
#
# Author(s): bartv
# Date: 2001-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
/sa11x0/v2_0/src/usbs_sa11x0_data.cxx
0,0 → 1,174
//==========================================================================
//
// 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 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): bartv
// 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
 
/sa11x0/v2_0/src/usbs_sa11x0.c
0,0 → 1,2551
//==========================================================================
//
// 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 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): bartv
// 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
};
/sa11x0/v2_0/ChangeLog
0,0 → 1,196
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.
 
//===========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//===========================================================================

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.