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 + -
显示快捷键?