usbs_d12.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,974 行 · 第 1/5 页

C
1,974
字号
//==========================================================================
//
//      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
};

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?