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