This is the mail archive of the ecos-discuss@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

dropped UDP packets on 8270 PowerPC board


Hi All,

A while back, I posted a message with the same subject above about a problem I was having with dropped UDP packets. Despite some helpful responses, I have not been able to resolve the problem.

I have been doing the UDP testing with two Freescale MPC8270 boards we developed based on the A&M Rattler 8280 PCI board. The Ethernet is implemented with FCC2.

I have added an RTP header with sequence numbers to each UDP packet being transmitted between the two boards. The application running on each board prints an error message when it detects a missing sequence number in the data it is receiving. If one of the boards is only receiving packets and the other is only transmitting them, I don't detect any missing packets, no matter how high the bitrate. However, when each board receives while simultaneously transmitting, I start to see missing sequence numbers in the received data at each board.

The boards both send 1200 byte UDP packets. In my test application there are two builds, A and B. I flash build A into one board and build B into the other board. Build A configures the board with IP address 10.10.0.20 and sends packets to 10.10.0.21 at 5 msec intervals which corresponds to a bitrate of about 2 Mbit/sec. Build B configures the board with IP address 10.10.0.21 and sends packets to 10.10.0.20 at 100 msec intervals which corresponds to a bitrate of about 100 kbit/sec. The dropped packets appear to happen in bursts on both boards, although there seem to be fewer dropped packets detected on the Build A board which is receiving data at a slower rate. If I configure both boards to send data at 2 Mbit/sec, I detect a lot more dropped packets on both boards.

I tried adding code to the ethernet driver (if_fcc.c) to check the RTP sequence numbers before they were passed to the network stack. The driver is also detecting the missing packets, so I don't think it is a problem with the network stack. I have also added debug messages to the ethernet driver to print a message on any RXBD error such as frame length, nonoctet, short frame, crc error, or overrun. I don't see any of these RXBD errors. I also enabled the FCC BSY interrupt which was not enabled in the driver, but I never get the interrupt.

I checked Freescale errata but I see no mention of problems with the FCC in ethernet mode.

I also added code to monitor the CPU load and it is usually in single digits.

I have attached the source for my test program and the modifications I made to if_fcc.c to debug the problem. To make the A build, add rtpa to the end of the make command. Add rtpb to make the B build.

I would appreciate any help possible. This problem is driving me nuts!

Best regards,

Paul Randall
Delta Digital Video
# Mostly written by Jonathan Larmour, Red Hat, Inc.
# Reference to ecos.mak added by John Dallaway, eCosCentric Limited, 2003-01-20
# This file is in the public domain and may be used for any purpose

# Usage:   make INSTALL_DIR=/path/to/ecos/install

INSTALL_DIR=$$(INSTALL_DIR) # override on make command line

include $(INSTALL_DIR)/include/pkgconf/ecos.mak

XCC           = $(ECOS_COMMAND_PREFIX)gcc
XCXX          = $(XCC)
XLD           = $(XCC)

CFLAGS        = -I$(INSTALL_DIR)/include
CXXFLAGS      = $(CFLAGS)
LDFLAGS       = -nostartfiles -L$(INSTALL_DIR)/lib -Ttarget.ld

#LDFLAGS       = -nostartfiles -L$(PKG_INSTALL_DIR)/lib -Wl,--gc-sections
#LIBS          = -Ttarget.ld -nostdlib

# RULES

.PHONY: all clean

all: rtp_test srec

rtpa: BUILD_DEF=-DA_BUILD
rtpa: clean rtp_test sreca
rtpb: BUILD_DEF=-DB_BUILD
rtpb: clean rtp_test srecb

clean:
	-rm -f  rtp_test rtp_test.o

sreca:
	powerpc-eabi-objcopy -O srec rtp_test rtp_test_a.srec

srecb:
	powerpc-eabi-objcopy -O srec rtp_test rtp_test_b.srec

rtp_test: rtp_test.o
	$(XLD) $(LDFLAGS) $(ECOS_GLOBAL_LDFLAGS) -o rtp_test rtp_test.o

%.o: %.c
	$(XCC) -c -o $*.o $(CFLAGS) $(ECOS_GLOBAL_CFLAGS) $(BUILD_DEF) $<

%.o: %.C
	$(XCC) -c -o $*.o $(CFLAGS) $(ECOS_GLOBAL_CFLAGS) $(BUILD_DEF) $<
/************************************************************************
*                                                                       *
*  Module Name: rtp_test.h                                              *
*                                                                       *
*  Description: main module for rtp test                                *
*                                                                       *
*  Date     By   Reason                                                 *
*                                                                       *
*  21Nov06  PWR  initial release
*                                                                       *
*************************************************************************/
#include "rtp_test.h"

void main_thread(void);
void start_main(cyg_addrword_t param);
void start_ethernet_rtp(cyg_addrword_t param);
int  Ethernet_Configure(void);
void Ethernet_RTP_Thread(void);
void Ethernet_Send_Rtp(void);

static char stack_main[STACK_SIZE];
static cyg_thread main_thread_data;
static cyg_handle_t main_thread_handle;

static char stack_ethernet_rtp[STACK_SIZE];
static cyg_thread ethernet_rtp_thread_data;
static cyg_handle_t ethernet_rtp_thread_handle;

int         rtp_socket;
struct      sockaddr_in local_rtp;
struct      sockaddr_in remote_rtp;
rtp_t       tx_rtp;
BOOL        bRtp_thread_run;

/*************************************************************************
*  Name: start_main()
*  Description:
*  Input:
*  Output:     na
*************************************************************************/
void start_main(cyg_addrword_t param)
{
    printf("main thread : starting\n");
    main_thread();
}

/*************************************************************************
*  Name: main_thread()
*  Description: main thread sends RTP packets to receiver
*  Input:
*  Output:     na
*************************************************************************/
void main_thread(void)
{
    int             rc;
    UINT32          loop_count           = 0;
#ifdef INCLUDE_CPU_LOAD
    UINT32          calibration          = 0;
    cyg_handle_t    cpuload_handle;
    cyg_cpuload_t   cpuload;
    cyg_uint32      average_point1s;
    cyg_uint32      average_1s;
    cyg_uint32      average_10s;
#endif

    printf("main thread : started\n");

    tx_rtp.version      = RTP_VERSION;
    tx_rtp.p            = 0;
    tx_rtp.x            = 0;
    tx_rtp.cc           = 0;
    tx_rtp.m            = 0;
    tx_rtp.pt           = 0;
    tx_rtp.seq          = 0;
    tx_rtp.ts           = 0;
    tx_rtp.ssrc         = 0;
    memset( tx_rtp.buffer, 0, UDP_MAX_SIZE );

    memset(&local_rtp,  0, sizeof(local_rtp));
    local_rtp.sin_family        = AF_INET;
    local_rtp.sin_len           = sizeof(local_rtp);
    local_rtp.sin_addr.s_addr   = htonl(INADDR_ANY);
    local_rtp.sin_port          = htons(8000);
    
    memset(&remote_rtp, 0, sizeof(remote_rtp));
    remote_rtp.sin_family       = AF_INET;
    remote_rtp.sin_len          = sizeof(remote_rtp);
    remote_rtp.sin_port         = htons(8000);
#if defined(A_BUILD)
    remote_rtp.sin_addr.s_addr  = (in_addr_t)0x0A0A0015;
#elif defined(B_BUILD)
    remote_rtp.sin_addr.s_addr  = (in_addr_t)0x0A0A0014;
#endif
    
#ifdef INCLUDE_CPU_LOAD
    cyg_cpuload_calibrate(&calibration);
    cyg_cpuload_create(&cpuload, calibration, &cpuload_handle);
#endif

    rc = Ethernet_Configure();

    rtp_socket = socket(AF_INET, SOCK_DGRAM, 0);  // domain, type, protocol
    if (rtp_socket < 0)
    {
        perror("rtp socket()");
        return;
    }

    // Bind RTP socket
    if( bind(rtp_socket, (struct sockaddr *) &local_rtp, sizeof(local_rtp)) < 0 )
    {
        perror("rtp bind()");
    }

    cyg_thread_resume(ethernet_rtp_thread_handle);

    while(TRUE)
    {
        loop_count++;
#ifdef INCLUDE_CPU_LOAD
        if( loop_count % 100 == 0 )
        {
            cyg_cpuload_get( cpuload_handle, &average_point1s, &average_1s, &average_10s );
            printf( "LOAD: %d\n", average_1s );
        }
#endif
        Ethernet_Send_Rtp();
#if defined(A_BUILD)
        cyg_thread_delay(5);
#elif defined(B_BUILD)
        cyg_thread_delay(100);
#endif

    }

    printf("main thread : finished\n");
}


/*************************************************************************
*  Name: start_ethernet_rtp()
*  Description: 
*  Input:
*  Output:     na
*************************************************************************/
void start_ethernet_rtp(cyg_addrword_t param)
{
    printf("ethernet rtp thread : starting\n");
    Ethernet_RTP_Thread();
}

/*************************************************************************
*  Name: resume_ethernet_rtp_thread()
*  Description: resume thread only if it is suspended
*  Input:
*  Output:     na
*************************************************************************/
int resume_ethernet_rtp_thread(void)
{
    cyg_thread_info info;
    cyg_uint16      id = 0;
    cyg_bool        rc;

    id = cyg_thread_get_id( ethernet_rtp_thread_handle );
    rc = cyg_thread_get_info( ethernet_rtp_thread_handle, id, &info );
    if( rc == false )
    {
        printf( "Error getting ethernet rtp thread info.  Cannot start thread.\n" );
    }
    else
    {
        if( info.state & 0x04 )                             // thread is suspended
            cyg_thread_resume( ethernet_rtp_thread_handle );    // Start ethernet
    }

    return 0;
}

/*************************************************************************
*  Name: Ethernet_RTP_Thread()
*  Description: 
*  Input:
*  Output:     na
*************************************************************************/
void Ethernet_RTP_Thread(void)
{
    rtp_t               *pRtp;
    struct sockaddr_in  remote_addr;
    int                 length              = 0;
    int                 remote_len          = sizeof(remote_addr);
    UINT32              last_seq            = 0;

    bRtp_thread_run = TRUE;

    pRtp = malloc( sizeof(rtp_t) );
    memset( pRtp, 0, sizeof(rtp_t) );

    // receive packets from ethernet and check sequence number
    while( bRtp_thread_run )
    {
        length = recvfrom(rtp_socket, pRtp, sizeof(rtp_t), 0,
                         (struct sockaddr *)&remote_addr, &remote_len);
                         
        if( length == 0 )
        {
            printf( "ethernet rtp thread : terminating normally\n" );
            break;
        }
        else if( length < 0 )
        {
            perror("recvfrom - ");
            printf( "ethernet rtp thread : terminating on error\n" );
            break;
        }

        if( (pRtp->seq != (last_seq+1)) && (pRtp->seq != 0) )
        {
            printf( "RTP SEQ error %08d %08d\n", last_seq, pRtp->seq );
        }
        last_seq = pRtp->seq;
        
        if( pRtp->seq % 1000 == 0 )
        {
            printf( "SEQ %d received\n", pRtp->seq );
        }
    }

    free( pRtp );

    printf("ethernet rtp thread : finished\n");

    bRtp_thread_run = FALSE;

    return;
}

/*************************************************************************
*  Name: Ethernet_Send_Rtp()
*  Description: Send data out on ethernet
*  Input:
*  Output:     na
*************************************************************************/
void Ethernet_Send_Rtp(void)
{
    int             len           = 0;

    tx_rtp.pt   = 97;
    tx_rtp.m    = 1;
    tx_rtp.seq++;

    len = sendto(rtp_socket, (UCHAR*)&tx_rtp, 1200, 0,
                 (struct sockaddr *)&remote_rtp, sizeof(remote_rtp));

    if ( len < 0 )
    {
        perror("rtp sendto");
    }

    return;
}

/*************************************************************************
*  Name: Ethernet_Configure()
*  Description: Set network parameters
*               The code in this function is based on the ecos function
*               init_all_network_interfaces in the
*               /packages/net/common/current/src/network_support.c file
*  Input:
*  Output:     na
*************************************************************************/
int Ethernet_Configure(void)
{
    struct in_addr  address;
    char            *pAddress;
    char            my_ip_addr_str[MAX_ASCII_MESSAGE]   = "0.0.0.0";
    char            my_netmask_str[MAX_ASCII_MESSAGE]   = "0.0.0.0";
    char            my_broadcast_str[MAX_ASCII_MESSAGE] = "0.0.0.0";
    char            my_gateway_str[MAX_ASCII_MESSAGE]   = "0.0.0.0";
    char            my_server_str[MAX_ASCII_MESSAGE]    = "0.0.0.0";

    // Initialize network using BOOTP/DHCP or with static addresses
    /*if( user.ip.bDHCP_enable == TRUE )
    {
        printf( "Configuring Ethernet using DHCP...\n" );
        init_all_network_interfaces();
    }
    else
    {*/
        printf( "Configuring Ethernet using static addresses...\n" );

#if defined(A_BUILD)
        address.s_addr = 0x0A0A0014;
#elif defined(B_BUILD)
        address.s_addr = 0x0A0A0015;
#endif
        pAddress = inet_ntoa( address );
        strcpy( my_ip_addr_str, pAddress );

        address.s_addr = 0xFF000000;
        pAddress = inet_ntoa( address );
        strcpy( my_netmask_str, pAddress );

        //address.s_addr = 0;
        //pAddress = inet_ntoa( address );
        //strcpy( my_broadcast_str, pAddress );

        address.s_addr = 0x0A0A0001;
        pAddress = inet_ntoa( address );
        strcpy( my_gateway_str, pAddress );

        if( !eth0_up )
        {
            eth0_up = TRUE;

            build_bootp_record( &eth0_bootp_data,
                                eth0_name,
                                my_ip_addr_str,
                                my_netmask_str,
                                my_broadcast_str,
                                my_gateway_str,
                                my_server_str );
            show_bootp( eth0_name, &eth0_bootp_data );
        }
        else
        {
            printf( "Ethernet Interface Already Initialized\n" );
        }

        if( eth0_up )
        {
            if ( !init_net(eth0_name, &eth0_bootp_data) )
            {
                printf("Ethernet initialization failed for eth0\n");
                eth0_up = FALSE;
            }
        }

#ifdef CYGPKG_NET_NLOOP
#if 0 < CYGPKG_NET_NLOOP
        {
            static int loop_init = 0;
            int i;
            if ( 0 == loop_init++ )
                for ( i = 0; i < CYGPKG_NET_NLOOP; i++ )
                    init_loopback_interface( i );
        }
#endif
#endif

    //}

    show_network_tables( printf );

    printf( "Ethernet Configured\n" );

    return ENOERR;
}

/*************************************************************************
*  Name: cyg_start()
*  Description: Starting point for application
*  Input:
*  Output:     na
*************************************************************************/
void cyg_start(void)
{

    cyg_thread_create(ETHERNET_RTP_THREAD_PRIORITY, // Priority
                      start_ethernet_rtp,           // entry
                      0,                            // entry parameter
                      "Ethernet rtp",               // Name
                      &stack_ethernet_rtp[0],       // Stack
                      STACK_SIZE,                   // Size
                      &ethernet_rtp_thread_handle,  // Handle
                      &ethernet_rtp_thread_data     // Thread data structure
            );
    //cyg_thread_resume(ethernet_rtp_thread_handle);  // Start it later

    cyg_thread_create(MAIN_THREAD_PRIORITY,     // Priority
                      start_main,               // entry
                      0,                        // entry parameter
                      "Main",                   // Name
                      &stack_main[0],           // Stack
                      STACK_SIZE,               // Size
                      &main_thread_handle,      // Handle
                      &main_thread_data         // Thread data structure
            );
    cyg_thread_resume(main_thread_handle);      // Start it now

    cyg_scheduler_start();
}

/************************************************************************
*                                                                       *
*  Module Name: rtp_test.h                                              *
*                                                                       *
*  Description: main module for rtp test                                *
*                                                                       *
*  Date     By   Reason                                                 *
*                                                                       *
*  21Nov06  PWR  initial release
*                                                                       *
*************************************************************************/

#ifndef _RTP_TEST_H_
#define _RTP_TEST_H_

#define INCLUDE_CPU_LOAD

#include <cyg/io/devtab.h>
#include <cyg/io/serialio.h>
#include <cyg/io/serial.h>
#include <cyg/io/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <network.h>
#include <arpa/inet.h>  // to get rid of inet_aton warning
#include "sysdef.h"
#ifdef INCLUDE_CPU_LOAD
#include <cyg/cpuload/cpuload.h>
#endif

//#define INCLUDE_CPU_LOAD
#define RTP_VERSION             2
#define RTP_HDR_SIZE            12
#define STACK_SIZE              (CYGNUM_HAL_STACK_SIZE_TYPICAL + 0x10000)
#define UDP_MAX_SIZE            1600
#define MAX_ASCII_MESSAGE       256
#define ETHERNET_RTP_THREAD_PRIORITY  18
#define MAIN_THREAD_PRIORITY          25

// RTP data header
typedef struct
{
   unsigned int version:2;   // protocol version
   unsigned int p:1;         // padding flag
   unsigned int x:1;         // header extension flag
   unsigned int cc:4;        // CSRC count
   unsigned int m:1;         // marker bit
   unsigned int pt:7;        // payload type
   unsigned int seq:16;      // sequence number
   UINT32       ts;          // timestamp
   UINT32       ssrc;        // synchronization source
   //UINT32       csrc[1];     // optional CSRC list
   UCHAR        buffer[UDP_MAX_SIZE];
} rtp_t;


int resume_ethernet_video_thread(void);
int resume_ethernet_video_rtcp_thread(void);
int resume_ethernet_audio_thread(void);
int resume_ethernet_audio_rtcp_thread(void);
int resume_ethernet_userdata_thread(void);
int resume_ethernet_transport_thread(void);
int resume_ethernet_rtsp_thread(void);
int resume_audio_input_thread(void);

#endif
/************************************************************************
*                                                                       *
*  Module Name: sysdef.h                                                *
*                                                                       *
*  Description: This file contains the system wide definitions
*
*  Date     By   Reason
*
*  13Dec04  PWR  initial release
*                                                                       *
*  Copyright Delta Information Systems Inc., Horsham, Pa., 2004         *
*  All Rights Reserved                                                  *
*************************************************************************/

#ifndef _SYSDEF_H_
#define _SYSDEF_H_

#include <cyg/kernel/kapi.h>            /* All the kernel specific stuff */

typedef void            (*FPTR)(void);
//typedef void            (*INT_HNDLR)(int);

typedef cyg_uint16      WORD;
typedef cyg_uint64      UINT64;
typedef cyg_uint32      UINT32;
typedef cyg_uint16      UINT16;
typedef cyg_uint8       UINT8;
typedef cyg_uint8       UCHAR;
typedef cyg_uint8       OCTET;
typedef cyg_uint32      BOOL;
typedef cyg_int32       INT32;
typedef cyg_int16       INT16;

#define ERROR           0xFF
#define OK              0x00

#define CARRIAGE_RETURN 0x0D
#define LINE_FEED       0x0A
#define BACKSPACE       0x08

#define UP              1
#define DOWN            0
#define NO_CHNG         2

#define TRUE            1
#define FALSE           0

#define LOW             0
#define HIGH            1

#define OFF             0
#define ON              1

#define DISABLE         0
#define ENABLE          1


#define EQ              ==
#define NE              !=

#define LT              <
#define GT              >
#define LE              <=
#define GE              >=

#define OR              ||
#define AND             &&


#define NULLPTR (char *)-1      /* null pointer for char ptr */
//#define NULL            0


// Bit Definitions
#define BIT00   0x00000001
#define BIT01   0x00000002
#define BIT02   0x00000004
#define BIT03   0x00000008
#define BIT04   0x00000010
#define BIT05   0x00000020
#define BIT06   0x00000040
#define BIT07   0x00000080
#define BIT08   0x00000100
#define BIT09   0x00000200
#define BIT10   0x00000400
#define BIT11   0x00000800
#define BIT12   0x00001000
#define BIT13   0x00002000
#define BIT14   0x00004000
#define BIT15   0x00008000
#define BIT16   0x00010000
#define BIT17   0x00020000
#define BIT18   0x00040000
#define BIT19   0x00080000
#define BIT20   0x00100000
#define BIT21   0x00200000
#define BIT22   0x00400000
#define BIT23   0x00800000
#define BIT24   0x01000000
#define BIT25   0x02000000
#define BIT26   0x04000000
#define BIT27   0x08000000
#define BIT28   0x10000000
#define BIT29   0x20000000
#define BIT30   0x40000000
#define BIT31   0x80000000

// Bit Mask Definitions
#define MASKB00 0xfffe
#define MASKB01 0xfffd
#define MASKB02 0xfffb
#define MASKB03 0xfff7
#define MASKB04 0xffef
#define MASKB05 0xffdf
#define MASKB06 0xffbf
#define MASKB07 0xff7f
#define MASKB08 0xfeff
#define MASKB09 0xfdff
#define MASKB10 0xfbff
#define MASKB11 0xf7ff
#define MASKB12 0xefff
#define MASKB13 0xdfff
#define MASKB14 0xbfff
#define MASKB15 0x7fff

#endif
//==========================================================================
//
//      dev/if_fcc.c
//
//      Fast ethernet device driver for PowerPC MPC8xxx (QUICC-II) boards
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002, 2003, 2004 Gary Thomas
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    gthomas
// Contributors: mtek, pfine
// Date:         2003-08-19
// Purpose:
// Description:  hardware driver for MPC8xxx FCC
//
//
//####DESCRIPTIONEND####
//
//==========================================================================

#define SEQUENCE_DEBUG
#define DDV_DEBUG

#include <pkgconf/devs_eth_powerpc_fcc.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/diag.h>

#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_cache.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_if.h>
#include <cyg/hal/mpc8xxx.h>

#include <cyg/io/eth/netdev.h>
#include <cyg/io/eth/eth_drv.h>

#ifdef CYGPKG_NET
#include <pkgconf/net.h>
#endif

#ifdef CYGPKG_DEVS_ETH_PHY
#include <cyg/io/eth_phy.h>
#endif

#include "fcc.h"

#ifdef CYGPKG_REDBOOT
#include <pkgconf/redboot.h>
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
#include <redboot.h>
#include <flash_config.h>
#endif
#endif

#ifdef CYGDAT_DEVS_FCC_ETH_INL
#include CYGDAT_DEVS_FCC_ETH_CDL   // platform configury
#include CYGDAT_DEVS_FCC_ETH_INL   // platform details
#else
#error "No board instance defined!"
#endif

#define ALIGN_TO_CACHE_LINES(x)  ( (long)((x) + 31) & 0xffffffe0 )

// Buffer descriptors are in dual ported RAM, which is marked non-cached
#define FCC_BDs_NONCACHED

#define os_printf diag_printf

// CONFIG_ESA and CONFIG_BOOL are defined in redboot/include/flash_config.h
#ifndef CONFIG_ESA
#define CONFIG_ESA 6      // ethernet address length ...
#endif

#ifndef CONFIG_BOOL
#define CONFIG_BOOL 1
#endif

static void          fcc_eth_int(struct eth_drv_sc *data);

// This ISR is called when the ethernet interrupt occurs
#ifdef CYGPKG_NET
static int
fcc_eth_isr(cyg_vector_t vector, cyg_addrword_t data, HAL_SavedRegisters *regs)
{
    struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
    struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;

    cyg_drv_interrupt_mask(qi->int_vector);
    return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Run the DSR
}
#endif

// Deliver function (ex-DSR) handles the ethernet [logical] processing
static void
fcc_eth_deliver(struct eth_drv_sc * sc)
{
#ifdef CYGPKG_NET
    struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;
#endif

    fcc_eth_int(sc);
#ifdef CYGPKG_NET
    //  Clearing the event register acknowledges FCC interrupt ...
    cyg_drv_interrupt_unmask(qi->int_vector);
#endif

}


// Initialize the interface - performed at system startup
// This function must set up the interface, including arranging to
// handle interrupts, etc, so that it may be "started" cheaply later.
static bool
fcc_eth_init(struct cyg_netdevtab_entry *dtp)
{
    struct eth_drv_sc *sc = (struct eth_drv_sc *)dtp->device_instance;
    struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;
    volatile t_Fcc_Pram  *fcc =  (volatile t_Fcc_Pram *)0;
    volatile t_EnetFcc_Pram *E_fcc;
    int i, fcc_chan;
    bool esa_ok;
    unsigned char *c_ptr;
    unsigned char _enaddr[6];
    unsigned long rxbase, txbase;
    struct fcc_bd *rxbd, *txbd;
    // The FCC seems rather picky about these...
    static long bd_base = DPRAM_FCC_BD;  // Available space for BDs
#ifdef CYGPKG_DEVS_ETH_PHY
    unsigned short phy_state = 0;
#endif

    // Set up pointers to FCC controller
    switch (qi->int_vector) {
    case CYGNUM_HAL_INTERRUPT_FCC1:
        qi->fcc_reg = &(IMM->fcc_regs[FCC1]);
        fcc =  (volatile t_Fcc_Pram *)((unsigned long)IMM + FCC1_PRAM_OFFSET);
        fcc_chan = FCC1_PAGE_SUBBLOCK;
        break;
    case CYGNUM_HAL_INTERRUPT_FCC2:
        qi->fcc_reg = &(IMM->fcc_regs[FCC2]);
        fcc =  (volatile t_Fcc_Pram *)((unsigned long)IMM + FCC2_PRAM_OFFSET);
        fcc_chan = FCC2_PAGE_SUBBLOCK;
        break;
    case CYGNUM_HAL_INTERRUPT_FCC3:
        qi->fcc_reg = &(IMM->fcc_regs[FCC3]);
        fcc =  (volatile t_Fcc_Pram *)((unsigned long)IMM + FCC3_PRAM_OFFSET);
        fcc_chan = FCC3_PAGE_SUBBLOCK;
        break;
    default:
        os_printf("Can't initialize '%s' - unknown FCC!\n", dtp->name);
        return false;
    }

    // just in case :  disable Transmit and Receive
    qi->fcc_reg->fcc_gfmr &= ~(FCC_GFMR_EN_Rx | FCC_GFMR_EN_Tx);

    // Try to read the ethernet address of the transciever ...
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
    esa_ok = flash_get_config(qi->esa_key, _enaddr, CONFIG_ESA);
#else
    esa_ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
                                         qi->esa_key, _enaddr, CONFIG_ESA);
#endif
    if (esa_ok) {
        memcpy(qi->enaddr, _enaddr, sizeof(qi->enaddr));
    } else {
        // No 'flash config' data available - use default
        os_printf("FCC_ETH - Warning! Using default ESA for '%s'\n", dtp->name);
    }

    // Initialize Receive Buffer Descriptors
    rxbase = bd_base;
    fcc->riptr = rxbase;           // temp work buffer
    fcc->mrblr = FCC_PRAM_MRBLR;   // Max Rx buffer
    fcc->rstate &= FCC_FCR_INIT;
    fcc->rstate |= FCC_FCR_MOT_BO;
    rxbase += 64;
    bd_base += sizeof(struct fcc_bd)*qi->rxnum + 64;
    rxbd = (struct fcc_bd *)(CYGARC_IMM_BASE + rxbase);
    fcc->rbase = (CYG_WORD)rxbd;
    c_ptr = qi->rxbuf;
    qi->rbase = rxbd;
    qi->rxbd  = rxbd;
    qi->rnext = rxbd;

    for (i = 0; i < qi->rxnum; i++, rxbd++) {
        rxbd->ctrl   = (FCC_BD_Rx_Empty | FCC_BD_Rx_Int);
        rxbd->length = 0;                   // reset
        c_ptr = (unsigned char *) ALIGN_TO_CACHE_LINES(c_ptr);
        rxbd->buffer = (volatile unsigned char *)c_ptr;
        c_ptr += CYGNUM_DEVS_ETH_POWERPC_FCC_BUFSIZE;
    }
    rxbd--;
    rxbd->ctrl |= FCC_BD_Rx_Wrap;

    // Initialize Transmit Buffer Descriptors
    txbase = bd_base;
    fcc->tiptr = txbase;   // in dual port RAM (see 28-11)
    fcc->tstate &= FCC_FCR_INIT;
    fcc->tstate |= FCC_FCR_MOT_BO;
    txbase += 64;
    bd_base += sizeof(struct fcc_bd)*qi->txnum + 64;
    txbd = (struct fcc_bd *)(CYGARC_IMM_BASE + txbase);
    fcc->tbase = (CYG_WORD)txbd;
    c_ptr = qi->txbuf;
    qi->tbase = txbd;
    qi->txbd  = txbd;
    qi->tnext = txbd;

    for (i = 0; i < qi->txnum; i++, txbd++) {
        txbd->ctrl   = (FCC_BD_Tx_Pad | FCC_BD_Tx_Int);
        txbd->length = 0;   // reset : Write before send
        c_ptr = (unsigned char *) ALIGN_TO_CACHE_LINES(c_ptr);
        txbd->buffer = (volatile unsigned char  *)c_ptr;
        c_ptr += CYGNUM_DEVS_ETH_POWERPC_FCC_BUFSIZE;
    }
    txbd--;
    txbd->ctrl |= FCC_BD_Tx_Wrap;

    // Ethernet Specific FCC Parameter RAM Initialization
    E_fcc = &(fcc->SpecificProtocol.e);
    E_fcc->c_mask   = FCC_PRAM_C_MASK; // (see 30-9)
    E_fcc->c_pres   = FCC_PRAM_C_PRES;
    E_fcc->crcec    = 0;
    E_fcc->alec     = 0;
    E_fcc->disfc    = 0;
    E_fcc->ret_lim  = FCC_PRAM_RETLIM;
    E_fcc->p_per    = FCC_PRAM_PER_LO;
    E_fcc->gaddr_h  = 0;
    E_fcc->gaddr_l  = 0;
    E_fcc->tfcstat  = 0;
    E_fcc->mflr     = FCC_MAX_FLR;

    E_fcc->paddr1_h = ((short)qi->enaddr[5] << 8) | qi->enaddr[4];
    E_fcc->paddr1_m = ((short)qi->enaddr[3] << 8) | qi->enaddr[2];
    E_fcc->paddr1_l = ((short)qi->enaddr[1] << 8) | qi->enaddr[0];

    E_fcc->iaddr_h  = 0;
    E_fcc->iaddr_l  = 0;
    E_fcc->minflr   = FCC_MIN_FLR;
    E_fcc->taddr_h  = 0;
    E_fcc->taddr_m  = 0;
    E_fcc->taddr_l  = 0;
    E_fcc->pad_ptr  = fcc->tiptr; // No special padding char ...
    E_fcc->cf_type  = 0;
    E_fcc->maxd1    = FCC_PRAM_MAXD;
    E_fcc->maxd2    = FCC_PRAM_MAXD;

    // FCC register initialization
    qi->fcc_reg->fcc_gfmr = FCC_GFMR_INIT;
    qi->fcc_reg->fcc_psmr = FCC_PSMR_INIT;
    qi->fcc_reg->fcc_dsr  = FCC_DSR_INIT;

#ifdef CYGPKG_NET
    // clear the events of FCCX
    qi->fcc_reg->fcc_fcce = 0xFFFF;
    // PWR added FCC_EV_RXB | FCC_EV_BSY for debug
    qi->fcc_reg->fcc_fccm = FCC_EV_TXE | FCC_EV_TXB | FCC_EV_RXF | FCC_EV_RXB | FCC_EV_BSY;

    // Set up to handle interrupts
    cyg_drv_interrupt_create(qi->int_vector,
                             0,  // Highest //CYGARC_SIU_PRIORITY_HIGH,
                             (cyg_addrword_t)sc, //  Data passed to ISR
                             (cyg_ISR_t *)fcc_eth_isr,
                             (cyg_DSR_t *)eth_drv_dsr,
                             &qi->fcc_eth_interrupt_handle,
                             &qi->fcc_eth_interrupt);
    cyg_drv_interrupt_attach(qi->fcc_eth_interrupt_handle);
    cyg_drv_interrupt_acknowledge(qi->int_vector);
    cyg_drv_interrupt_unmask(qi->int_vector);
#else

    // Mask the interrupts
    qi->fcc_reg->fcc_fccm = 0;
#endif

    // Issue Init RX & TX Parameters Command for FCCx
    while ((IMM->cpm_cpcr & CPCR_FLG) != CPCR_READY_TO_RX_CMD);
    IMM->cpm_cpcr = CPCR_INIT_TX_RX_PARAMS |
        fcc_chan |
        CPCR_MCN_FCC |
        CPCR_FLG;              /* ISSUE COMMAND */
    while ((IMM->cpm_cpcr & CPCR_FLG) != CPCR_READY_TO_RX_CMD);

#ifdef CYGPKG_DEVS_ETH_PHY
    // Operating mode
    if (!_eth_phy_init(qi->phy)) {
        return false;
    }
#ifdef CYGSEM_DEVS_ETH_POWERPC_FCC_RESET_PHY
    _eth_phy_reset(qi->phy);
#endif
    phy_state = _eth_phy_state(qi->phy);
    os_printf("FCC %s: ", sc->dev_name);
    if ((phy_state & ETH_PHY_STAT_LINK) != 0) {
        if ((phy_state & ETH_PHY_STAT_100MB) != 0) {
            // Link can handle 100Mb
            os_printf("100Mb");
            if ((phy_state & ETH_PHY_STAT_FDX) != 0) {
                os_printf("/Full Duplex");
            }
        } else {
            // Assume 10Mb, half duplex
            os_printf("10Mb");
        }
    } else {
        os_printf("/***NO LINK***\n");
#ifdef CYGPKG_REDBOOT
        return false;
#endif
    }
    os_printf("\n");
#endif // CYGPKG_DEVS_ETH_PHY

    // Initialize upper level driver for ecos
    (sc->funs->eth_drv->init)(sc, (unsigned char *)&qi->enaddr);

    return true;
}

//
// This function is called to "start up" the interface.  It may be called
// multiple times, even when the hardware is already running.  It will be
// called whenever something "hardware oriented" changes and should leave
// the hardware ready to send/receive packets.
//
static void
fcc_eth_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
  struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;

  // Enable the device :
  // Set the ENT/ENR bits in the GFMR -- Enable Transmit/Receive
  qi->fcc_reg->fcc_gfmr |= (FCC_GFMR_EN_Rx | FCC_GFMR_EN_Tx);

}

//
// This function is called to shut down the interface.
//
static void
fcc_eth_stop(struct eth_drv_sc *sc)
{
  struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;

  // Disable the device :
  // Clear the ENT/ENR bits in the GFMR -- Disable Transmit/Receive
  qi->fcc_reg->fcc_gfmr &= ~(FCC_GFMR_EN_Rx | FCC_GFMR_EN_Tx);
}


//
// This function is called for low level "control" operations
//
static int
fcc_eth_control(struct eth_drv_sc *sc, unsigned long key,
                void *data, int length)
{
    struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;
    volatile t_Fcc_Pram  *fcc =  (volatile t_Fcc_Pram *)0;
    volatile t_EnetFcc_Pram *E_fcc;
    int i, fcc_chan = FCC2_PAGE_SUBBLOCK;
    struct eth_drv_mc_list *mc_list;
#ifdef CYGPKG_DEVS_ETH_PHY
    unsigned short phy_state = 0;
#endif

    switch (qi->int_vector) {
    case CYGNUM_HAL_INTERRUPT_FCC1:
        fcc =  (volatile t_Fcc_Pram *)((unsigned long)IMM + FCC1_PRAM_OFFSET);
        fcc_chan = FCC1_PAGE_SUBBLOCK;
        break;
    case CYGNUM_HAL_INTERRUPT_FCC2:
        fcc =  (volatile t_Fcc_Pram *)((unsigned long)IMM + FCC2_PRAM_OFFSET);
        fcc_chan = FCC2_PAGE_SUBBLOCK;
        break;
    case CYGNUM_HAL_INTERRUPT_FCC3:
        fcc =  (volatile t_Fcc_Pram *)((unsigned long)IMM + FCC3_PRAM_OFFSET);
        fcc_chan = FCC3_PAGE_SUBBLOCK;
        break;
    default:
        break;
    }

    E_fcc = &(fcc->SpecificProtocol.e);

    switch (key) {
    case ETH_DRV_SET_MAC_ADDRESS:
        return 0;
        break;
#ifdef ETH_DRV_SET_MC_ALL
    case ETH_DRV_SET_MC_ALL:
        return 0;
        break;
#endif
#ifdef ETH_DRV_SET_MC_LIST
    case ETH_DRV_SET_MC_LIST:
        mc_list = (struct eth_drv_mc_list *)data;
        for( i=0; i<mc_list->len; i++ )
        {
            E_fcc->taddr_h = ((short)mc_list->addrs[i][5] << 8) | mc_list->addrs[i][4];
            E_fcc->taddr_m = ((short)mc_list->addrs[i][3] << 8) | mc_list->addrs[i][2];
            E_fcc->taddr_l = ((short)mc_list->addrs[i][1] << 8) | mc_list->addrs[i][0];
            IMM->cpm_cpcr = CPCR_SET_GRP_ADDR |
                fcc_chan |
                CPCR_MCN_FCC |
                CPCR_FLG;              /* ISSUE COMMAND */
            while ((IMM->cpm_cpcr & CPCR_FLG) != CPCR_READY_TO_RX_CMD);
        }
        return 0;
        break;
#endif

#ifdef ETH_DRV_GET_IF_STATS_UD
    case ETH_DRV_GET_IF_STATS_UD: // UD == UPDATE
#endif
        // drop through
#ifdef ETH_DRV_GET_IF_STATS
    case ETH_DRV_GET_IF_STATS:
#endif
#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD)
    {
        struct ether_drv_stats *p = (struct ether_drv_stats *)data;
        // Chipset entry is no longer supported; RFC1573.
        for ( i = 0; i < SNMP_CHIPSET_LEN; i++ )
            p->snmp_chipset[i] = 0;

        strcpy( p->description, "QUICC-II (MPC82xx) FCC Ethernet" );
        CYG_ASSERT( 48 > strlen(p->description), "Description too long" );

#ifdef CYGPKG_DEVS_ETH_PHY
        // Determine link status, speed, and duplex
        phy_state = _eth_phy_state(qi->phy);
        if ((phy_state & ETH_PHY_STAT_LINK) != 0) {
            p->operational = 3;         // LINK UP
            if ((phy_state & ETH_PHY_STAT_100MB) != 0) {
                // Link can handle 100Mb
                p->speed = 100000000;
                if ((phy_state & ETH_PHY_STAT_FDX) != 0) {
                    p->duplex = 3;      // 3 = DUPLEX
                }
                else {
                    p->duplex = 2;      // 2 = SIMPLEX
                }
            } else {
                // Assume 10Mb, half duplex
                p->speed = 10000000;
                p->duplex = 2;          // 2 = SIMPLEX
            }
        } else {
            p->operational = 2;         // LINK DOWN
            p->duplex      = 1;         // 1 = UNKNOWN
            p->speed       = 0;
        }
#else
        p->operational = 2;             // LINK DOWN
        p->duplex      = 1;             // 1 = UNKNOWN
        p->speed       = 0;
#endif

        /*{
            p->supports_dot3        = false;

            // Those commented out are not available on this chip.

            p->tx_good              = qi->tx_good             ;
            //p->tx_max_collisions    = qi->tx_max_collisions ;
            p->tx_late_collisions   = qi->tx_late_collisions  ;
            p->tx_underrun          = qi->tx_underrun         ;
            p->tx_carrier_loss      = qi->tx_carrier_loss     ;
            p->tx_deferred          = qi->tx_deferred         ;
            //p->tx_sqetesterrors   = qi->tx_sqetesterrors    ;
            //p->tx_single_collisions = qi->tx_single_collisions;
            //p->tx_mult_collisions   = qi->tx_mult_collisions  ;
            //p->tx_total_collisions  = qi->tx_total_collisions ;
            p->rx_good              = qi->rx_good             ;
            p->rx_crc_errors        = qi->rx_crc_errors       ;
            p->rx_align_errors      = qi->rx_align_errors     ;
            p->rx_resource_errors   = qi->rx_resource_errors  ;
            p->rx_overrun_errors    = qi->rx_overrun_errors   ;
            p->rx_collisions        = qi->rx_collisions       ;
            p->rx_short_frames      = qi->rx_short_frames     ;
            p->rx_too_long_frames   = qi->rx_long_frames      ;
            //p->rx_symbol_errors   = qi->rx_symbol_errors    ;

            p->interrupts           = qi->interrupts          ;
            p->rx_count             = qi->rx_count            ;
            p->rx_deliver           = qi->rx_deliver          ;
            p->rx_resource          = qi->rx_resource         ;
            p->rx_restart           = qi->rx_restart          ;
            p->tx_count             = qi->tx_count            ;
            p->tx_complete          = qi->tx_complete         ;
            p->tx_dropped           = qi->tx_dropped          ;
        }*/

        p->tx_queue_len = CYGNUM_DEVS_ETH_POWERPC_FCC_TxNUM;

        return 0; // OK
    }
#endif

    default:
        return 1;
        break;
    }
}


//
// This function is called to see if another packet can be sent.
// It should return the number of packets which can be handled.
// Zero should be returned if the interface is busy and can not send any more.
//
static int
fcc_eth_can_send(struct eth_drv_sc *sc)
{
  struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;
  volatile struct fcc_bd *txbd = qi->txbd;
#ifndef FCC_BDs_NONCACHED
  int cache_state;
#endif

#ifndef FCC_BDs_NONCACHED
  HAL_DCACHE_IS_ENABLED(cache_state);
  if (cache_state) {
    HAL_DCACHE_INVALIDATE(fcc_eth_txring,
                          8*CYGNUM_DEVS_ETH_POWERPC_FCC_TxNUM);
  }
#endif

  return ((txbd->ctrl & (FCC_BD_Tx_TC | FCC_BD_Tx_Ready)) == 0);
}

//
// This routine is called to send data to the hardware.
static void
fcc_eth_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
             int total_len, unsigned long key)
{
  struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;
  struct fcc_bd *txbd, *txfirst;
  volatile char *bp;
  int i, txindex;
  int cache_state;

  HAL_DCACHE_IS_ENABLED(cache_state);
#ifndef FCC_BDs_NONCACHED
  if (cache_state) {
    HAL_DCACHE_INVALIDATE(fcc_eth_txring,
                          8*CYGNUM_DEVS_ETH_POWERPC_FCC_TxNUM);
  }
#endif

  // Find a free buffer
  txbd = txfirst = qi->txbd;
  while (txbd->ctrl & FCC_BD_Tx_Ready) {
    // This buffer is busy, move to next one
    if (txbd->ctrl & FCC_BD_Tx_Wrap) {
      txbd = qi->tbase;
    } else {
      txbd++;
    }
    if (txbd == txfirst) {
#ifdef CYGPKG_NET
      panic ("No free xmit buffers");
#else
      os_printf("FCC Ethernet: No free xmit buffers\n");
#endif
    }
  }

  // Remember the next buffer to try
  if (txbd->ctrl & FCC_BD_Tx_Wrap) {
    qi->txbd = qi->tbase;
  } else {
    qi->txbd = txbd+1;
  }

  txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
  qi->txkey[txindex] = key;

  // Set up buffer
  txbd->length = total_len;
  bp = txbd->buffer;
  for (i = 0;  i < sg_len;  i++) {
    memcpy((void *)bp, (void *)sg_list[i].buf, sg_list[i].len);
    bp += sg_list[i].len;
  }

  // Make sure no stale data buffer ...
  if (cache_state) {
    HAL_DCACHE_FLUSH(txbd->buffer, txbd->length);
  }

  // Send it on it's way
  txbd->ctrl |= FCC_BD_Tx_Ready | FCC_BD_Tx_Last | FCC_BD_Tx_TC;

#ifndef FCC_BDs_NONCACHED
  if (cache_state) {
    HAL_DCACHE_FLUSH(fcc_eth_txring,
                     8*CYGNUM_DEVS_ETH_POWERPC_FCC_TxNUM);
  }
#endif

}

//
// This function is called when a packet has been received.  It's job is
// to prepare to unload the packet from the hardware.  Once the length of
// the packet is known, the upper layer of the driver can be told.  When
// the upper layer is ready to unload the packet, the internal function
// 'fcc_eth_recv' will be called to actually fetch it from the hardware.
//
static void
fcc_eth_RxEvent(struct eth_drv_sc *sc)
{
  struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;
  struct fcc_bd *rxbd;
  int cache_state;
#ifdef DDV_DEBUG
    volatile t_Fcc_Pram  *fcc =  (volatile t_Fcc_Pram *)0;
    volatile t_EnetFcc_Pram *E_fcc;
    static CYG_WORD last_disfc;
#endif
#ifdef SEQUENCE_DEBUG
    cyg_uint16 sequence_num, port;
    static cyg_uint16 last_sequence_num = 0;
#endif

  HAL_DCACHE_IS_ENABLED(cache_state);
#ifndef FCC_BDs_NONCACHED
  if (cache_state) {
    HAL_DCACHE_INVALIDATE(fcc_eth_rxring,
                          8*CYGNUM_DEVS_ETH_POWERPC_FCC_RxNUM);
  }
#endif

#ifdef DDV_DEBUG
    // added for debug PWR 1/10/2006
    switch (qi->int_vector) {
    case CYGNUM_HAL_INTERRUPT_FCC1:
        fcc =  (volatile t_Fcc_Pram *)((unsigned long)IMM + FCC1_PRAM_OFFSET);
        break;
    case CYGNUM_HAL_INTERRUPT_FCC2:
        fcc =  (volatile t_Fcc_Pram *)((unsigned long)IMM + FCC2_PRAM_OFFSET);
        break;
    case CYGNUM_HAL_INTERRUPT_FCC3:
        fcc =  (volatile t_Fcc_Pram *)((unsigned long)IMM + FCC3_PRAM_OFFSET);
        break;
    default:
        break;
    }

    E_fcc = &(fcc->SpecificProtocol.e);

    if( E_fcc->disfc != last_disfc )
    {
        diag_printf( "FCC Discarded frames %u\n", E_fcc->disfc );
        last_disfc = E_fcc->disfc;
    }
#endif

  rxbd = qi->rnext;
  while ((rxbd->ctrl & FCC_BD_Rx_Empty) == 0) {
    qi->rxbd = rxbd;  // Save for callback

#ifdef SEQUENCE_DEBUG
    port = *(cyg_uint16 *)(rxbd->buffer+36);
    if( port == 8000 )
    {
    sequence_num = *(cyg_uint16 *)(rxbd->buffer+44);
    if( sequence_num != (last_sequence_num + 1) )
      diag_printf("FCC SEQ %d %d\n", last_sequence_num, sequence_num);
    last_sequence_num = sequence_num;
    }
#endif

    // This is the right way of doing it, but dcbi has a bug ...
    //    if (cache_state) {
    //      HAL_DCACHE_INVALIDATE(rxbd->buffer, rxbd->length);
    //    }
    if ((rxbd->ctrl & FCC_BD_Rx_ERRORS) == 0) {
        (sc->funs->eth_drv->recv)(sc, rxbd->length);
#if 1 // Coherent caches?
        if (cache_state) {
            HAL_DCACHE_FLUSH(rxbd->buffer, rxbd->length);
        }
#endif
    }
    else
    {
#if 1
       diag_printf("FCC Rx error BD: %x/%x- ", rxbd, rxbd->ctrl);
       if ((rxbd->ctrl & FCC_BD_Rx_LG) != 0) diag_printf("Frame Length/");
       if ((rxbd->ctrl & FCC_BD_Rx_NO) != 0) diag_printf("Nonoctet/");
       if ((rxbd->ctrl & FCC_BD_Rx_SH) != 0) diag_printf("Short Frame/");
       if ((rxbd->ctrl & FCC_BD_Rx_CR) != 0) diag_printf("CRC Error/");
       if ((rxbd->ctrl & FCC_BD_Rx_OV) != 0) diag_printf("Overrun/");
       diag_printf("\n");
#endif
    }
    // Reset control flags to known [empty] state, clearing error bits
    if (rxbd->ctrl & FCC_BD_Rx_Wrap) {
      rxbd->ctrl = FCC_BD_Rx_Empty | FCC_BD_Rx_Int | FCC_BD_Rx_Wrap;
      rxbd = qi->rbase;
    } else {
      rxbd->ctrl = FCC_BD_Rx_Empty | FCC_BD_Rx_Int;
      rxbd++;
    }
  }
  // Remember where we left off
  qi->rnext = (struct fcc_bd *)rxbd;

  // Make sure no stale data
#ifndef FCC_BDs_NONCACHED
  if (cache_state) {
    HAL_DCACHE_FLUSH(fcc_eth_rxring,
                     8*CYGNUM_DEVS_ETH_POWERPC_FCC_RxNUM);
  }
#endif

}

//
// This function is called as a result of the "eth_drv_recv()" call above.
// It's job is to actually fetch data for a packet from the hardware once
// memory buffers have been allocated for the packet.  Note that the buffers
// may come in pieces, using a scatter-gather list.  This allows for more
// efficient processing in the upper layers of the stack.
//
static void
fcc_eth_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
  struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;
  unsigned char *bp;
  int i;

  bp = (unsigned char *)qi->rxbd->buffer;

  for (i = 0;  i < sg_len;  i++) {
    if (sg_list[i].buf != 0) {
      memcpy((void *)sg_list[i].buf, bp, sg_list[i].len);
      bp += sg_list[i].len;
    }
  }

}

static void
fcc_eth_TxEvent(struct eth_drv_sc *sc, int stat)
{
  struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;
  struct fcc_bd *txbd;
  int txindex;
#ifndef FCC_BDs_NONCACHED
  int cache_state;
#endif

#ifndef FCC_BDs_NONCACHED
  // Make sure no stale data
  HAL_DCACHE_IS_ENABLED(cache_state);
  if (cache_state) {
    HAL_DCACHE_INVALIDATE(fcc_eth_txring,
                          8*CYGNUM_DEVS_ETH_POWERPC_FCC_TxNUM);
  }
#endif

  txbd = qi->tnext;
  // Note: TC field is used to indicate the buffer has/had data in it
  while ( (txbd->ctrl & (FCC_BD_Tx_TC | FCC_BD_Tx_Ready)) == FCC_BD_Tx_TC ) {
      if ((txbd->ctrl & FCC_BD_Tx_ERRORS) != 0) {
#if 0
          if((txbd->ctrl & FCC_BD_Tx_ERRORS) != FCC_BD_Tx_CSL) {
          diag_printf("FCC Tx error BD: %x/%x- ", txbd, txbd->ctrl);
          if ((txbd->ctrl & FCC_BD_Tx_LC) != 0) diag_printf("Late Collision/");
          if ((txbd->ctrl & FCC_BD_Tx_RL) != 0) diag_printf("Retry limit/");
//          if ((txbd->ctrl & FCC_BD_Tx_RC) != 0) diag_printf("Late Collision/");
          if ((txbd->ctrl & FCC_BD_Tx_UN) != 0) diag_printf("Underrun/");
          if ((txbd->ctrl & FCC_BD_Tx_CSL) != 0) diag_printf("Carrier Lost/");
          diag_printf("\n");
          }
#endif
      }

    txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
    (sc->funs->eth_drv->tx_done)(sc, qi->txkey[txindex], 0);
    txbd->ctrl &= ~FCC_BD_Tx_TC;
    if (txbd->ctrl & FCC_BD_Tx_Wrap) {
      txbd = qi->tbase;
    } else {
      txbd++;
    }
  }
  // Remember where we left off
  qi->tnext = (struct fcc_bd *)txbd;

  // Make sure no stale data
#ifndef FCC_BDs_NONCACHED
  if (cache_state) {
    HAL_DCACHE_FLUSH(fcc_eth_txring,
                     8*CYGNUM_DEVS_ETH_POWERPC_FCC_TxNUM);
  }
#endif

}

//
// Interrupt processing
//
static void
fcc_eth_int(struct eth_drv_sc *sc)
{
  struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;
  unsigned short iEvent;

  while ((iEvent = qi->fcc_reg->fcc_fcce) != 0){
    // Clear pending interrupts (writing 1's to this register)
    qi->fcc_reg->fcc_fcce = iEvent;
    // Tx Done or Tx Error
    if ( iEvent & (FCC_EV_TXB | FCC_EV_TXE) ) {
      fcc_eth_TxEvent(sc, iEvent);
    }
    // Complete or non-complete frame receive
    if (iEvent & (FCC_EV_RXF | FCC_EV_RXB) ) {
      fcc_eth_RxEvent(sc);
    }
#ifdef DDV_DEBUG
    // check added PWR 1/27/06
    if (iEvent & FCC_EV_BSY)
    {
      diag_printf("FCC BSY\n");
    }
#endif
  }
}

//
// Interrupt vector
//
static int
fcc_eth_int_vector(struct eth_drv_sc *sc)
{
    struct fcc_eth_info *qi = (struct fcc_eth_info *)sc->driver_private;
    return (qi->int_vector);
}


-- 
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]