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 -- 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 -- 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#### |
//=========================================================================== |