/****************************************************************************
 *           INTEL CORPORATION PROPRIETARY INFORMATION
 *  This software is supplied under the terms of a license agreement or
 *  nondisclosure agreement with Intel Corporation and may not be copied
 *  or disclosed except in accordance with the terms of that agreement.
 *      Copyright (c) 1999-2000 Intel Corporation.  All Rights Reserved.
 ***************************************************************************/

/***************************************************************************
 * $Source: /mnt/bigpine/src/oem/IXP1200/CoreLibs/OctalMAC_21440/Attic/PseudoDrvEnd.cpp,v $
 * $Revision: 4 $
 * $Date: 5/15/00 1:53p $
 *
 * Description:
 *    This file contains the implementation of a pseudo END (Enhanced
 *    Network Driver) for VxWorks.  The bottom half of this driver interfaces
 *    with the iXP1200 microengines for raw receive/transmit functionality.
 *
 *    The driver operates in "polled recieve" mode regardless of what the mux 
 *    library above requests.  To do this, it spawns a tPethPollRcv task to 
 *    monitor the incoming packet queue and dispense them to the appropriate
 *    instance of this driver.  All instances of this driver share this
 *    one task.
 *
 *    To compile this driver, PETH_NUM_DEVS must be defined to be an
 *    integer representing the maximum number of instances of this
 *    driver that will ever be created.  PETH_DRV must also be defined,
 *    or else the entire file is not seen by the compiler.
 *
 *    This driver assumes familiarity with the IXP1200 Evaluation Board
 *    reference design.  If you are not familiar with terms like descriptor,
 *    microengine, etc. you should see the IXP1200 Software Reference Manual 
 *    for more details before reading this driver.
 *
 *    Although certain places in this driver use C++, the intent is to
 *    keep as much of the driver as possible completely in C.  This is
 *    done to keep the burden of moving this driver into the kernel as
 *    small as possible.  The consequence is that goto's are used in
 *    places where exceptions might otherwise be appropriate.
 *
 * Known Limitations:
 *    - Many of the Ioctl's do not work properly, esp. regarding polled
 *      mode and setting/changing driver flags
 *    - The driver does not properly listen to the given set of multicast
 *      addresses.  It passes all packets delivered to it from the microengines
 *      up to the kernel.
 *    - The driver does not currently work with the Gigabit ports on the
 *      IXP1200 EB.  This is due only to the fact that the code does not
 *      yet parse the gigabit port's packet descriptors.
 *    - By default, the MAC addresses assigned are 00:0x:CA:FE:BA:BE, where
 *      x = the unit number.  This can be overridden by supplying a routine
 *      to return the MAC address and defining it to be SYS_ENET_ADDR_GET
 *      before compiling this driver.
 *    - Incoming packets are physically copied from the microengine
 *      descriptor location into a VxWorks network memory pool.  This is
 *      done to properly align the incoming packets (as required by
 *      the kernel).  A much more efficient solution would be to simply
 *      overlay an mBlk data structure on the packet from the microengines
 *      and deliver it to the kernel that way.  This would allow us to
 *      effectively remove the memory pool allocated in pethInitMem().
 *      The prerequisites for such a change are either (i) the microengines
 *      align the incoming packets so that the IP header falls on a double
 *      word boundary, or (ii) the kernel properly deals with unaligned IP
 *      headers.
 *
 * Contents:
 *    pethEndLoad - the only externally visible routine
 ***************************************************************************/

#define PETH_NUM_DEVS 8

#include "ipproto.h"
#include "rtm_mvr.h"
#include "end.h"
#include "muxLib.h"

#include "vxWorks.h"
#include "assert.h"
#include "cacheLib.h"

#include "end.h"			/* Common END structures. */

#include "errno.h"
#include "etherLib.h"
#include "etherMultiLib.h" 		/* multicast stuff. */


#include "intLib.h"
#include "iv.h"
#include "logLib.h"
#include "m2Lib.h"
#include "memLib.h"
#include "muxLib.h"

#include "net/if.h"
#include "net/if_subr.h"
#include "net/mbuf.h"
#include "net/protosw.h"
#include "net/route.h"
#include "netBufLib.h"

#include "netLib.h"
#include "netinet/if_ether.h"
#include "netinet/in.h"
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/ip.h"
#include "semLib.h"

#include "stdlib.h"
#include "sys/ioctl.h"
#include "sys/socket.h"
#include "sysLib.h"
#include "taskLib.h"
#include "wdLib.h"

/* IXP1200 specific entries */

#include "mem_map.h"
#include "hal_sram.h"
#include "hal_sdram.h"

/* 
 * END_MACROS is undefined before we include endLib.h so that we will
 * get all of the endLib routines as functions instead of macros
 */
#undef END_MACROS
#include "endLib.h"
#include "lstLib.h"		 /* Needed to maintain multicast list. */






extern "C" {

/**************************************************************************** 
 ****************************************************************************
 * Configuration items 
 *  In the following subsections, various MACROs and constants are
 *  defined that control the behavior of the driver.  Each section is
 *  individually documented.
 ****************************************************************************/

/****************************************************************************
 * Microengine configuration items
 *
 * PETH_ENDIAN_HACK           - If defined, the incomming and outgoing packets
 *                              will have every 4 bytes endian-swapped
 * PETH_NUM_QUEUE_DESCRIPTORS - The size of the freelist maintained in
 *                              SRAM by the microengine reference design.
 *                              This MUST match the freelist size specified
 *                              to the assembler when building the microengine
 *                              code.  It is used here simply to validate
 *                              the descriptors passed into the driver.
 * PETH_DESCRIPTOR_STRIDE     - 4, which is the stride of descriptors used
 *                              in the microengine reference design.  If
 *                              the stride in the microengines changes this
 *                              value MUST change.
 * PETH_MAX_DESCRIPTOR        - Based on PETH_NUM_QUEUE_DESCRIPTORS, this
 *                              is the calculated as the maximum possible
 *                              descriptor value.  It is stride times the 
 *                              number of descriptors.
 * PETH_VALID_DESCRIPTOR      - Given a descriptor, this macro evalutes to
 *                              0 if the descriptor is invalid (i.e., <=0 or
 *                              greater than PETH_MAX_DESCRIPTOR), or nonzero
 *                              otherwise.
 * PETH_ALIGNED_DESCRIPTOR    - Given a desciptor, this macro evaluates to
 *                              0 if the descriptor does not fall on
 *                              the stride (i.e. is not evenly divisible by
 *                              PETH_DESCRIPTOR_STRIDE).
 * PETH_MAX_PACKET_SIZE       - Defined as the maximum size of any packet
 *                              received from the microengines
 * PETH_CORE_PORT             - Defined as the incoming port that the poller
 *                              will listen on (18)
 * PETH_GET_QUEUE             - Given a queue number, evaluates to the 
 *                              core address used to access the associated
 *                              packet queue
 */
#define PETH_ENDIAN_HACK
#define PETH_NUM_QUEUE_DESCRIPTORS 300
#define PETH_DESCRIPTOR_STRIDE 4
#define PETH_MAX_DESCRIPTOR \
    (PETH_NUM_QUEUE_DESCRIPTORS * PETH_DESCRIPTOR_STRIDE)
#define PETH_VALID_DESCRIPTOR(desc) \
    ((PETH_MAX_DESCRIPTOR >= (desc)) && (0 < (desc)))
#define PETH_ALIGNED_DESCRIPTOR(desc) \
    (((desc) % PETH_DESCRIPTOR_STRIDE) == 0)
#define PETH_MAX_PACKET_SIZE (ETHERMTU + ENET_HDR_REAL_SIZ)
#define PETH_CORE_PORT (18)
#define PETH_GET_QUEUE(num) \
    (SRAM_QUEUE_DESCRIPTOR_BASE + ((num)<<4))


/**************************************************************************** 
 * Initialization items
 * 
 * PETH_SPEED           - The speed of the interface in bits/sec.  This value
 *                        is hardcoded and would obviously need to change with
 *                        the inclusion of the gigabit ports.
 * PETH_DRV_NAME        - The string name of this driver.  This is the name
 *                        the driver will use to register with the kernel
 * PETH_DEV_NAME_LEN    - The length of PETH_DRV_NAME
 */
#define PETH_SPEED        10000000
#define PETH_DEV_NAME     "peth"
#define PETH_DEV_NAME_LEN 5


/**************************************************************************** 
 * Polling items
 * PETH_RCV_POLL_TASK_NAME     - The name (string) of the task spawned that
 *                               will poll for incoming packets
 * PETH_RCV_POLL_PRIORITY      - The priority of the polling task, currently
 *                               set arbitrarily, but lower (higher value)
 *                               than tNetTask
 * PETH_RCV_POLL_RATE          - The number of ticks the poller will pause
 *                               between each execution of the polling loop
 * PETH_RCV_POLL_STACK_SIZE    - The size (in bytes) of the stack allocated
 *                               for the polling task
 */
#define PETH_RCV_POLL_TASK_NAME  "tPethPollRcv"
#define PETH_RCV_POLL_PRIORITY   75
#define PETH_RCV_POLL_RATE       5
#define PETH_RCV_POLL_STACK_SIZE 10000
LOCAL int pethPollerTID = 0;   /* Assigned when the polling task is spawned */

/****************************************************************************
 * Descriptor accessing and modifying items
 * PETH_DESC_GET_NEXT          - Given a descriptor and a storage location,
 *                               retrieves the next link in the queue
 * PETH_DESC_GET_FLAGS         - Given a descriptor and a storage location,
 *                               retrieves the flags associated with the
 *                               descriptor
 * PETH_DESC_GET_PDATA         - Given a descriptor, evaluates to a pointer to
 *                               the packet data associated with the descriptor
 * PETH_DESC_SET_NEXT          - Given a descriptor and next link descriptor,
 *                               write the next link into SRAM
 * PETH_DESC_SET_FLAGS         - Given a descriptor and flags, write the flags
 *                               into SRAM
 * PETH_DESC_CORETOUENG        - Given a descriptor obtained from pop (i.e.
 *                               is relative to how the core addresses
 *                               SRAM), this macro evaluates to the argument
 *                               such that it is now the same value that
 *                               the microengines would use.
 * PETH_DESC_POSTPOP           - Given a descriptor obtained from a pop (and
 *                               relative to the microengine addressing
 *                               scheme), this macro modifies the descriptor 
 *                               for use.  It should always be called after a
 *                               sram->pop().
 * PETH_DESC_PREPUSH           - Given a descriptor, undoes any actions taken
 *                               by PETH_DESC_POSTPOP.  It should always be
 *                               called before a sram->push()
 *                               
 *
 * PETH_DESCFLGS_GET_LEN       - Given the descriptor flags, evaluates to the
 *                               length of the packet
 */
/* The follow define the bit-level layout of the descriptor flags */
#define PETH_DESCFLGS_ELCNT_MASK   0x7f
#define PETH_DESCFLGS_ELCNT_SHIFT  0
#define PETH_DESCFLGS_QWORDS_MASK  0xE000
#define PETH_DESCFLGS_QWORDS_SHIFT 13
#define PETH_DESCFLGS_BYTES_MASK   0x1C00
#define PETH_DESCFLGS_BYTES_SHIFT  10
#define PETH_DESCFLGS_FPORT_MASK   0x80000000
#define PETH_DESCFLGS_FPORT_SHIFT  31
#define PETH_DESCFLGS_INPORT_MASK  0xf000000
#define PETH_DESCFLGS_INPORT_SHIFT 24

#define PETH_DESCFLGS_GET_ELCNT(descFlags) \
    (((descFlags) & PETH_DESCFLGS_ELCNT_MASK) >> PETH_DESCFLGS_ELCNT_SHIFT)
#define PETH_DESCFLGS_GET_QWORDS(descFlags) \
    (((descFlags) & PETH_DESCFLGS_QWORDS_MASK) >> PETH_DESCFLGS_QWORDS_SHIFT)
#define PETH_DESCFLGS_GET_BYTES(descFlags) \
    (((descFlags) & PETH_DESCFLGS_BYTES_MASK) >> PETH_DESCFLGS_BYTES_SHIFT)
#define PETH_DESCFLGS_GET_INPORT(descFlags) \
    (((descFlags) & PETH_DESCFLGS_INPORT_MASK) >> PETH_DESCFLGS_INPORT_SHIFT)

#define PETH_DESCFLGS_GET_LEN(descFlags) \
    ( ((PETH_DESCFLGS_GET_ELCNT(descFlags) - 1) * 64) + \
      (PETH_DESCFLGS_GET_QWORDS(descFlags) * 8) + \
      (PETH_DESCFLGS_GET_BYTES(descFlags) + 1) )

#define PETH_DESC_GET_NEXT(desc, next) \
    (SRAM_READ((desc) + SRAM_BUFF_DESCRIPTOR_BASE, &(next)))
#define PETH_DESC_GET_FLAGS(desc, descFlags) \
    (SRAM_READ((desc) + 1 + SRAM_BUFF_DESCRIPTOR_BASE, &(descFlags)))
#define PETH_DESC_GET_PDATA(desc) \
    (SDRAM_NON_PREFETCH_BEGIN + ((SDRAM_PKT_BUFFER_BASE + ((desc)<<6))<<3))
#define PETH_DESC_SET_NEXT(desc, next) \
    (SRAM_WRITE((desc) + SRAM_BUFF_DESCRIPTOR_BASE, (next)))
#define PETH_DESC_SET_FLAGS(desc, descFlags) \
    (SRAM_WRITE((desc) + 1 + SRAM_BUFF_DESCRIPTOR_BASE, (descFlags)))
#define PETH_DESC_CORETOUENG(desc) \
    ( (((desc) - SRAM_READ_WRITE_BEGIN) >> 2) - SRAM_BUFF_DESCRIPTOR_BASE )
#define PETH_DESC_POSTPOP(desc) ((desc) += 1)
#define PETH_DESC_PREPUSH(desc) ((desc) -= 1)



/**************************************************************************** 
 * MAC Addresses
 * SYS_ENET_ADDR_GET         - If not otherwise defined, this macro will
 *                             assign the MAC address of 00:0x:CA:FE:BA:BE,
 *                             where x is the given unit number
 * END_HADDR                 - Returns the MAC address for the device by
 *                             searching the associated MIB2 data structure
 * END_HADDR_LEN             - Returns the length (in bytes) of the MAC
 *                             address for the device by searching the 
 *                             associated MIB2 data structure
 */
#ifndef SYS_ENET_ADDR_GET
#define SYS_ENET_ADDR_GET(pAddress, unit) { \
    ((char *)pAddress)[0] = 0; \
    ((char *)pAddress)[1] = unit; \
    ((char *)pAddress)[2] = 0xCA; \
    ((char *)pAddress)[3] = 0xFE; \
    ((char *)pAddress)[4] = 0xBA; \
    ((char *)pAddress)[5] = 0xBE; \
    }
#endif SYS_ENET_ADDR_GET
#define END_HADDR(pEnd)	    ((pEnd)->mib2Tbl.ifPhysAddress.phyAddress)
#define END_HADDR_LEN(pEnd) ((pEnd)->mib2Tbl.ifPhysAddress.addrLength)


/**************************************************************************** 
 ****************************************************************************
 * Driver control structure.  
 *   Each instance of the driver is allocated
 *   one of these structures (as required for END compliance).  The first
 *   field must be of type END_OBJ.
 *
 *   In addition, all instances of these control structures are also
 *   maintained in the pethDevices array.  This array is used by the poller 
 *   for demultiplexing purposes.
 ****************************************************************************/
typedef struct peth_device {
    END_OBJ     end;			/* The class we inherit from. */
    int		unit;			/* unit number */
    UCHAR	enetAddr[6];		/* ethernet address */
    UCHAR       enabled;                /* If the driver is enabled */
} PETHEND_DEVICE;

LOCAL PETHEND_DEVICE *pethDevices[PETH_NUM_DEVS];
LOCAL UCHAR pethDevicesLoadedCount = 0; /* Tells how many instances of the
                                           driver have been loaded */


/****************************************************************************
 ****************************************************************************
 * VxWorks network memory pool
 *
 * All instances of the driver share the memory buffers, this is done to
 * help minimize the total size of the pool while still having reasonable
 * response to bursts of packets.  This pool itself is maintained because
 * incoming packets must be copied from the microengines so that the
 * IP header can be properly double-word aligned.
 *
 * The NULL and 0 fields in the following variables are filled in
 * during pethInitMem()
 */
LOCAL M_CL_CONFIG pethMclConfig = {0, 0, NULL, 0};
LOCAL CL_DESC     pethClDescTbl[] = {
    /* 
    clusterSize	  num		      memArea   memSize
    -----------	  ----	  	      -------   -------
    */
    {128,         PETH_NUM_DEVS*10,   NULL,     0},
    {512,         PETH_NUM_DEVS*5,    NULL,     0},
    {2048,        PETH_NUM_DEVS*3,    NULL,	0}
};
LOCAL int      pethClDescTblNumEnt = (NELEMENTS(pethClDescTbl));
LOCAL NET_POOL pethNetPool;
LOCAL int      pethNetPoolInitialized = 0;


/****************************************************************************
 ****************************************************************************
 * DEBUG MACROS 
 *  Define DRV_DEBUG to include debugging support.  Even though
 *  DRV_DEBUG support is on, you must still set pethDebug to
 *  some combination of the DRV_DEBUG_XXX values to actually see
 *  the debugging messages.
 *
 *  The default state of debugging is DRV_DEBUG true, pethDebug = 0
 ****************************************************************************/
/* #undef DRV_DEBUG */
#define DRV_DEBUG

#ifdef	DRV_DEBUG
#define DRV_DEBUG_OFF		0x0000
#define DRV_DEBUG_RX		0x0001
#define	DRV_DEBUG_TX		0x0002
#define DRV_DEBUG_INT		0x0004
#define	DRV_DEBUG_POLL		(DRV_DEBUG_POLL_RX | DRV_DEBUG_POLL_TX)
#define	DRV_DEBUG_POLL_RX	0x0008
#define	DRV_DEBUG_POLL_TX	0x0010
#define	DRV_DEBUG_LOAD		0x0020
#define	DRV_DEBUG_IOCTL		0x0040
#define DRV_DEBUG_POLL_REDIR	0x10000
#define	DRV_DEBUG_LOG_NVRAM	0x20000
//int     pethDebug = DRV_DEBUG_OFF;
int     pethDebug = DRV_DEBUG_RX | DRV_DEBUG_TX ;

#define DRV_LOG(FLG, X0, X1, X2, X3, X4, X5, X6) \
        if (pethDebug & (FLG)) \
            logMsg(X0, X1, X2, X3, X4, X5, X6);

#else  /*DRV_DEBUG*/
#define DRV_LOG(DBG_SW, X0, X1, X2, X3, X4, X5, X6)
#endif /*DRV_DEBUG*/


/**************************************************************************** 
 ****************************************************************************
 * Forward declarations:
 *  pethEndLoad is the only externally visible routine
 *  The routines marked STUB are not currently implemented and thus
 *  are stubbed out (see the Stub section at the end of the file)
 ****************************************************************************/
//END_OBJ* 	pethEndLoad (char* initString, void*);
LOCAL STATUS	pethInitParse(PETHEND_DEVICE * pDrvCtrl, char * initString);
LOCAL STATUS	pethInitMem (PETHEND_DEVICE * pDrvCtrl);
LOCAL STATUS	pethUnload (PETHEND_DEVICE* pDrvCtrl);

LOCAL STATUS	pethRecv (PETHEND_DEVICE *pDrvCtrl, UINT32 descriptor);
LOCAL STATUS	pethSend (PETHEND_DEVICE* pDrvCtrl, M_BLK_ID pBuf);

LOCAL int	pethIoctl (PETHEND_DEVICE* pDrvCtrl, int cmd, caddr_t data);

LOCAL STATUS	pethStart (PETHEND_DEVICE* pDrvCtrl);
LOCAL STATUS	pethStop (PETHEND_DEVICE* pDrvCtrl);
LOCAL void	pethPoller(void);

LOCAL STATUS    pethFreeDescriptor(unsigned int descriptor);
LOCAL int       pethIsAnyDriverActive(void);

/* STUBS */
LOCAL STATUS	pethMCastAddrAdd (PETHEND_DEVICE* pDrvCtrl, char* pAddress);
LOCAL STATUS	pethMCastAddrDel (PETHEND_DEVICE* pDrvCtrl, char* pAddress);
LOCAL STATUS	pethMCastAddrGet (PETHEND_DEVICE* pDrvCtrl, 
                                  MULTI_TABLE* pTable);
LOCAL void	pethConfig (PETHEND_DEVICE *pDrvCtrl);
LOCAL STATUS	pethPollSend (PETHEND_DEVICE* pDrvCtrl, M_BLK_ID pBuf);
LOCAL STATUS	pethPollReceive (PETHEND_DEVICE* pDrvCtrl, M_BLK_ID pBuf);


/****************************************************************************
 ****************************************************************************
 * Driver functions:
 *    These routines are provided to the kernel during registration.
 *    These routines are the same across all instances of the driver.
 ****************************************************************************/
LOCAL NET_FUNCS pethFuncTable = {
    (FUNCPTR)pethStart,         /* Function to start the device. */
    (FUNCPTR)pethStop,          /* Function to stop the device. */
    (FUNCPTR)pethUnload,        /* Unloading function for the driver. */
    (FUNCPTR)pethIoctl,         /* Ioctl function for the driver. */
    (FUNCPTR)pethSend,          /* Send function for the driver. */
    (FUNCPTR)pethMCastAddrAdd,  /* Multicast address add */
    (FUNCPTR)pethMCastAddrDel,  /* Multicast address delete */
    (FUNCPTR)pethMCastAddrGet,  /* Multicast table retrieve */
    (FUNCPTR)pethPollSend,      /* Polling send function for the driver. */
    (FUNCPTR)pethPollReceive,   /* Polling receive function for the driver. */
    endEtherAddressForm,        /* Put address info into a packet.  */
    endEtherPacketDataGet,      /* Get a pointer to packet data. */
    endEtherPacketAddrGet       /* Get packet addresses. */
};


/**************************************************************************
 * Function: pethEndLoad()
 *
 * Parameters:
 *     initString   - Used for two distinct purposes:
 *                      1) Called with an empty, but allocated string.
 *                         This indicates that the routine should copy
 *                         the driver name into initString and return 0.
 *                      2) Called with an allocated, non-empty string.
 *                         This indicates that the routine should try
 *                         an initialize a new instance of the driver.
 *                         The initString in this case should be of the
 *                         form "<unit#>:" (where <unit#> is the instance
 *                         of the driver.
 *
 * Description:
 *   This routine can be called in two modes.  If it is called with an
 *   empty, but allocated, string, it places the name of the device
 *   ("peth") into the input argument.  If it is called with an allocated,
 *   non-empty string, the routine attempts to load the driver.  This
 *   includes allocating space for a new device data structure, allocating
 *   packet memory, etc.  This routine will register the driver routines
 *   with the kernel.
 *
 * Returns:
 *   If called in context 1, returns 0
 *   If called in context 2, either returns an END object pointer representing
 *   the device data structure for this instance of the driver, or NULL,
 *   indicating an error condition.
 *
 * Context:
 *
 * Comments:
 */
END_OBJ* pethEndLoad(char* initString, void *vd) {
    PETHEND_DEVICE 	*pDrvCtrl;
    
    DRV_LOG(DRV_DEBUG_LOAD, "Loading peth...\n", 1, 2, 3, 4, 5, 6);
    
    /* No string, indicates an error (case 2) */
    if (NULL == initString) {
        return (NULL);
    }
    
    /* Case 1 - return the name of the driver */
    if ('\0' == initString[0]) {
        DRV_LOG(DRV_DEBUG_LOAD, "Returning name of: %s\n", 
                (int) PETH_DEV_NAME, 2, 3, 4, 5, 6);
        bcopy((char *)PETH_DEV_NAME, initString, PETH_DEV_NAME_LEN);
        return (0);
    }
    
    /* Case 2 - create a new instance of the driver */
    /* Allocate the device structure */
    pDrvCtrl = (PETHEND_DEVICE *)calloc(sizeof(PETHEND_DEVICE), 1);
    if (NULL == pDrvCtrl) {
	goto errorExit;
    }
    
    /* The driver is disabled until start is called */
    pDrvCtrl->enabled = 0;

    /* Parse the init string, filling in the device structure.
     * pethInitParse will validate the the initString 
     */
    if (ERROR == pethInitParse(pDrvCtrl, initString)) {
	goto errorExit;
    }

    /* Save the structure for later use by the poller/demultiplexor */
    pethDevices[pDrvCtrl->unit] = pDrvCtrl;
    
    /* Have the BSP hand us our address. */
    SYS_ENET_ADDR_GET(&(pDrvCtrl->enetAddr), pDrvCtrl->unit);
    
    /* Initialize the END and MIB2 parts of the driver structure */
    if (ERROR == 
        END_OBJ_INIT(&pDrvCtrl->end, (DEV_OBJ *)pDrvCtrl, 
                     PETH_DEV_NAME,
                     pDrvCtrl->unit, &pethFuncTable,
                     "Pseudo Enet iXP1200 Enhanced Network Driver")
        || 
        ERROR ==
        END_MIB_INIT(&pDrvCtrl->end, 
                     M2_ifType_ethernet_csmacd,
                        &pDrvCtrl->enetAddr[0], 6, ETHERMTU,
                        PETH_SPEED)
        ) {
	goto errorExit;
    }
    
    /* Perform memory allocation */
    if (ERROR == pethInitMem (pDrvCtrl)) {
	goto errorExit;
    }
    
    /* set the flags to indicate readiness */
    END_OBJ_READY (&pDrvCtrl->end,
                   IFF_UP | IFF_RUNNING | IFF_NOTRAILERS | IFF_BROADCAST
                   | IFF_PROMISC | IFF_MULTICAST | IFF_SIMPLEX);
    
    DRV_LOG (DRV_DEBUG_LOAD, "Done loading peth...\n", 1, 2, 3, 4, 5, 6);
    
    /* We have a new device, so increment a global count, this count
       is used to tell us when to remove global resources (see pethUnload) */
    ++pethDevicesLoadedCount;
    return (&pDrvCtrl->end);
    
 errorExit:
    logMsg("%s: Error in load\n", (int) __FUNCTION__, 2, 3, 4, 5, 6);
    if (NULL != pDrvCtrl) {
	free ((char *)pDrvCtrl);
    }
    
    return NULL;
}


/**************************************************************************
 * Function: pethInitParse()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure to be
 *                  filled in
 *     initString - A pointer to the init string of the format
 *                  "<unit#>:" where <unit#> is the instance of this driver
 *                  which must be <= 0 < PETH_NUM_DEVS
 *
 * Description:
 *     This routine parses the given string and fills in the driver
 *     control stucture.  The semantic values of the string (i.e. the
 *     unit number) are checked.
 *
 * Returns:
 *     OK if the string was correctly parsed and all of the values
 *     were validated, ERROR otherwise.
 *
 * Context:
 *
 * Comments:
 */
STATUS pethInitParse(PETHEND_DEVICE * pDrvCtrl, char * initString) {
    char *	tok;
    char *	pHolder = NULL;
    
    /* Parse the initString */
    if (NULL == initString) {
        return ERROR;
    }

    /* Unit number. */
    tok = strtok_r (initString, ":", &pHolder);
    if (NULL == tok) {
	return ERROR;
    }
    pDrvCtrl->unit = atoi(tok);
    /* Validate the unit number */
    if ((0 > pDrvCtrl->unit) || (pDrvCtrl->unit >= PETH_NUM_DEVS)) {
        return ERROR;
    }
    DRV_LOG(DRV_DEBUG_LOAD, "Unit # = %d\n", pDrvCtrl->unit, 2, 3, 4, 5, 6);
    
    DRV_LOG(DRV_DEBUG_LOAD, "Processed all arugments\n", 1, 2, 3, 4, 5, 6);
    
    return OK;
}


/**************************************************************************
 * Function: pethInitMem()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure
 *
 * Description:
 *     This routine allocates the single shared memory structure common
 *     to all instances of the driver.  It can be called as many times
 *     as desired, but will only allocate successfully allocate memory
 *     once.  The only exception is if pethUnload is called, this routine 
 *     can be called again and it will allocate memory.  The pDrvCtrl
 *     structure is updated to point to the common memory structure.
 *
 * Returns:
 *     OK if the function executed successfully, ERROR otherwise.  Note
 *     that OK simply means that there is a common memory structure
 *     available, not that this particular call allocated that memory.
 *
 * Context:
 *
 * Comments:
 */
LOCAL STATUS pethInitMem(PETHEND_DEVICE * pDrvCtrl) {
    int lastAllocCnt; /* Do not change after the for() loop below */

    DRV_LOG(DRV_DEBUG_LOAD, "Executing %s\n", (int) __FUNCTION__, 2, 3, 4, 
            5, 6);
        
    /* All drivers share the same netpool */
    if (!pethNetPoolInitialized) {
        int totClBlkCnt = 0;

        /* Allocate memory for each of the cluster block sizes.
         * Each cluster (see pethClDesTbl above) is allocated
         * to be of size: (clusterSize + sizeof(long)) * # clusters
         * These cluster (memory regions) are stored in the array
         * pethClDescTbl.  See netBufLib for more information.
         * Upon exit from this loop:
         *   - pethClDescTbl[i] will contain the allocated memory
         *   - totClBlkCnt will contain the total number of clusters blocks
         *                 across all allocations
         *   - lastAllocCnt will contain the last index of pethClDescTbl
         *                  that successfully allocated
         */
        for (lastAllocCnt = 0;
             lastAllocCnt < pethClDescTblNumEnt; 
             ++lastAllocCnt) {
            /* Count the total # of blks to that we know how many mBlks
               to allocate later */
            totClBlkCnt += pethClDescTbl[lastAllocCnt].clNum;

            /* Allocate the cluster */
            pethClDescTbl[lastAllocCnt].memSize = 
                pethClDescTbl[lastAllocCnt].clNum * 
                (pethClDescTbl[lastAllocCnt].clSize + sizeof(long));
            pethClDescTbl[lastAllocCnt].memArea = (char *)
                malloc(pethClDescTbl[lastAllocCnt].memSize);
            if (NULL == pethClDescTbl[lastAllocCnt].memArea) {
                logMsg("%s: Failed to allocated buffer memory\n",
                       (int) __FUNCTION__, 2, 3, 4, 5, 6);
                goto cleanupClusters;
            }
        }
        
        /* Determine the mBlk appropriate parameters to pass to netPoolInit.
         * You have to have at least the same number of mBlks as you
         * do clusters.  You have to have 1:1 correspondence between clusters
         * and cluster blocks.
         * Each mBlk also requires memory, the size of which is
         *   M_BLK_SZE + sizeof(long)
         * Each cluster block requires memory, the size of which is
         *   CL_BLK_SZ
         */
        pethMclConfig.mBlkNum  = totClBlkCnt;  
        pethMclConfig.clBlkNum = totClBlkCnt; 
        pethMclConfig.memSize  = (pethMclConfig.mBlkNum * 
                                  (M_BLK_SZ + sizeof(long))) +
                                 (pethMclConfig.clBlkNum * CL_BLK_SZ);

        if (NULL == 
            (pethMclConfig.memArea = (char *) malloc(pethMclConfig.memSize))) {
            logMsg("%s: Could not allocate mBlk memory\n",
                   (int) __FUNCTION__, 2, 3, 4, 5, 6);
            goto cleanupMblk;
        }

        /* Create the shared pool */
        if (ERROR ==
            netPoolInit(&pethNetPool, &pethMclConfig, &pethClDescTbl[0], 
                        pethClDescTblNumEnt, NULL)) {
            logMsg("%s: Could not init buffering\n",
                   (int) __FUNCTION__, 2, 3, 4, 5, 6);
            goto cleanupMblk;
        }
        
        pethNetPoolInitialized = 1;
    }
    pDrvCtrl->end.pNetPool = &pethNetPool;
    return OK;
    
 cleanupMblk:
    free(pethMclConfig.memArea);
    pethMclConfig.memArea = NULL;
 cleanupClusters:
    for (--lastAllocCnt; lastAllocCnt >= 0; --lastAllocCnt) {
        free(pethClDescTbl[lastAllocCnt].memArea);
        pethClDescTbl[lastAllocCnt].memArea = NULL;
    }
    return (ERROR);
}


/**************************************************************************
 * Function: pethUnload()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure for this
 *                  instance of the driver
 *
 * Description:
 *     Frees resources allocated with this driver.  If there are no
 *     other instances of the driver loaded, the common memory pool is
 *     also free'd.
 *
 * Returns:
 *     OK upon successful unloading, ERROR otherwise
 * Context:
 *
 * Comments:
 */
LOCAL STATUS pethUnload(PETHEND_DEVICE* pDrvCtrl) {
    DRV_LOG(DRV_DEBUG_LOAD, "%s called\n", (int) __FUNCTION__, 2, 3, 4, 5, 6); 

    /* Remove the device from our list of all devices */
    pethDevices[pDrvCtrl->unit] = NULL;

    /* Stop the device - this will just be done to stop the poller if
       if this is the last living instance */
    if (ERROR == pethStop(pDrvCtrl)) {
        DRV_LOG(DRV_DEBUG_LOAD, "%s: failed to stop driver\n",
                (int) __FUNCTION__, 2, 3, 4, 5, 6);
        return ERROR;
    }

    /* Free up the common memory pool if no other drivers are loaded */
    if (--pethDevicesLoadedCount <= 0) {
        pethDevicesLoadedCount = 0; /* Just to ensure our sanity */

        /* Tells pethInitMem() that the global pool is gone */
        pethNetPoolInitialized = 0;

        /* Free the global memory pool */
        if (ERROR == netPoolDelete(&pethNetPool)) {
            DRV_LOG(DRV_DEBUG_LOAD, "%s: failed to free global memPool\n",
                    (int) __FUNCTION__, 2, 3, 4, 5, 6);
            return ERROR;
        }
    }

    return OK;
}


/**************************************************************************
 * Function: pethRecv()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure for this
 *                  instance of the driver
 *     descriptor - The descriptor for the incoming packet (see IXP1200
 *                  Software Reference Manual)
 *
 * Description:
 *     This routine handles one incoming packet from the microengines.
 *     The packet is described by the descriptor argument, which,
 *     implicit in its value, contains a pointer to the actual packet
 *     data, as well as several flags that describe the length of 
 *     the packet, which port it was received on etc.
 *
 *     The routine copies the packet data from the descriptor into
 *     the driver's memory pool.  It then hands the packet off to
 *     the kernel and finally free's the descriptor (by pushing it back
 *     onto the free list).
 *
 * Returns:
 *     OK if the packet was successfully handed off to the kernel,
 *     ERROR otherwise.
 *
 * Context:
 *
 * Comments:
 *    This routine currently does not properly handle fast port (gigabit)
 *    packets.
 *    This routine currently does not properly mark broadcast packets.
 *    This routine, ideally, would not copy the incoming packets but
 *    rather overlay an mBlk on top and just pass that up to the kernel.
 *    This is not done because the kernel requires the IP header to
 *    be double-word aligned and the microengines cannot perform this
 *    alignment.
 */
LOCAL STATUS pethRecv(PETHEND_DEVICE *pDrvCtrl, UINT32 descriptor) {
    M_BLK_ID 	pMblk = NULL;
    CL_BLK_ID	pClBlk;
    UINT32      descFlags;
    int         len;
    char *      pData, pNewCluster;

   printf("My pethRecv function called\n");
    DRV_LOG(DRV_DEBUG_RX, "%s: descriptor = 0x%x\n",
            (int) __FUNCTION__, (int) descriptor, 3, 4, 5, 6);

    /* Sanity checks */
    if (!pDrvCtrl->enabled) {
        logMsg("Packet received on disabled interface: %d, dropping\n",
               pDrvCtrl->unit, 2, 3, 4, 5, 6);
        goto cleanRXD;
    }

    if (!PETH_VALID_DESCRIPTOR(descriptor)) {
        logMsg("Bad descriptor %x, dropping incoming packet\n", descriptor, 2, 
               3, 4, 5, 6);
        return ERROR;
    }
    
    /* Get the descriptor flags to obtain the packet len, type, etc. */
    PETH_DESC_GET_FLAGS(descriptor, descFlags);

    /* Get pointer to packet */
    pData = (char *) PETH_DESC_GET_PDATA(descriptor);

    /* Find out how many bytes are in the current packet */
    len = PETH_DESCFLGS_GET_LEN(descFlags);
    DRV_LOG(DRV_DEBUG_RX, "%s: descFlags = %x, len = %d\n",
            (int) __FUNCTION__, descFlags, len, 4, 5, 6);


    /* Sanity check on the length of the packet */
    if ((0 == len) || (len > PETH_MAX_PACKET_SIZE)) {
        DRV_LOG(DRV_DEBUG_RX, "%s: packet len (%d) bad!, dropping...\n", 
                (int) __FUNCTION__, len, 3, 4, 5, 6);
        goto cleanRXD;
    }

    /* We don't yet handle fast port packets, drop them */
    if (descFlags & PETH_DESCFLGS_FPORT_MASK) {
        DRV_LOG(DRV_DEBUG_RX, "%s: dropping fast port packet\n",
                (int) __FUNCTION__, 2, 3, 4, 5, 6);
        goto cleanRXD;
    }

    /* Get memory to copy the packet into, we must do a copy because
       Vxworks requires the IP header to be aligned on a double-word
       boundary.  To accomplish this, we know that the memory returned
       from pethTupleGet is double-word aligned, and that the packet
       from the microengines is double-word - 2 bytes aligned, so
       we allocate a block of memory that is 2 bytes larger than the
       packet length.  This lets up copy the microengine packet into
       this new buffer + 2 bytes*/
    if (NULL == (pMblk = netTupleGet(&pethNetPool, len+2, M_DONTWAIT,
                                     MT_DATA, TRUE))) {
        logMsg("%s: Could not get new mBlk/ClDesc/cluster tuple\n",
               (int) __FUNCTION__, 2, 3, 4, 5, 6);
        END_ERR_ADD(&pDrvCtrl->end, MIB2_IN_ERRS, +1);
        goto cleanRXD;
    }
    DRV_LOG(DRV_DEBUG_RX, "%s: pNewCluster = %x\n",
            (int) __FUNCTION__, pNewCluster, 3, 4, 5, 6);

    /* BUG - Need to properly mark the packet as broadcast, etc */
    END_ERR_ADD (&pDrvCtrl->end, MIB2_IN_UCAST, +1);

    /* Align the mBlk */
    pMblk->mBlkHdr.mData  += 2;    /* Here is the 2-byte alignment */
    pMblk->mBlkHdr.mLen    = len;
    pMblk->mBlkHdr.mFlags |= M_PKTHDR;
    pMblk->mBlkPktHdr.len  = len;

    /* Copy the data */
#ifdef PETH_ENDIAN_HACK
    for (int i=0; i < len; i+=4) {
        *((UINT32 *)(pData + i)) = htonl(*((UINT32 *)(pData+i)));
    }
#endif PETH_ENDIAN_HACK
    bcopy(pData, pMblk->mBlkHdr.mData, len);

	for(int i=0;i<len;i++)
		printf("%x",*(pMblk->mBlkHdr.mData+i));
	//Policy Decision
//		policy_decision(pMblk->mBlkHdr.mData, len);
	printf("\n");

//	printf("PACKET RECEIVED= 0x%x\n",pMblk->mBlkHdr.mData);
    /* Call the upper layer's receive routine. */
    END_RCV_RTN_CALL(&pDrvCtrl->end, pMblk);

cleanRXD:
    pethFreeDescriptor(descriptor);
    return (OK);
}


/**************************************************************************
 * Function: pethSend()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the device control structure for this
 *                  instance of the driver
 *     pMblk      - An MBLK pointer containing the packet data to send.
 *
 * Description:
 *     This routine transmits the given packet data.  This is accomplished
 *     by allocating a new packet descriptor, copying the MBLK packet
 *     (which may be fragmented in memory) into the descriptor, then
 *     enqueueing the descriptor on the proper packet-queue.  Finally,
 *     this routine will tell the microengines to look at the queue for 
 *     new packets.  The microengines will then dequeue the descriptor
 *     and transmit the packet.
 * Returns:
 *     ERROR when it is not able to allocate memory for the outgoing packet,
 *     otherwise OK.  Note it returns OK even in cases where the packet
 *     is not transmitted (such as when the packet is passed in without
 *     an L2 header).
 *
 * Context:
 *
 * Comments:
 *     This routine requires the complete packet (L2 header and all) to
 *     be in the pMblk.  If it is passed a packet that does not have the
 *     L2 header, it will simply drop the packet and not try and perform
 *     the address resolution itself.
 */
LOCAL STATUS pethSend(PETHEND_DEVICE *pDrvCtrl, M_BLK_ID pMblk) {
    int         len = 0;
    UINT32      descriptor, descFlags;
    UINT32      queue, queueCount;
    UINT32      headTail, tail;
    char *      pData;
    SramUnit    *sramUnit = SRAM_Attach();

   printf("My pethSend function called\n");

    DRV_LOG (DRV_DEBUG_TX, "%s called\n", (int) __FUNCTION__, 2, 3, 4, 5, 6);

    /* Sanity checks */
    if (NULL == sramUnit) {
        printf("%s: could not attach to the sram unit\n", __FUNCTION__,
               2, 3, 4, 5, 6);
        goto errorAndCleanTXD;
    }

    /* Do not transmit complete packets, i.e. we do not do address
       resolution */
    if (pMblk->mBlkHdr.mType == MT_IFADDR) {
        DRV_LOG(DRV_DEBUG_TX, 
                "%s: mBlk chain is of MT_IFADDR type...dropping\n",
                (int) __FUNCTION__, 2, 3, 4, 5, 6);
        netMblkClChainFree(pMblk->mBlkHdr.mNext);
        return OK; /* Don't try again */
    }
    
    /* Get a new descriptor where we can write the packet data */
    sramUnit->pop(0, &descriptor);
    descriptor = PETH_DESC_CORETOUENG(descriptor);
    if (!PETH_ALIGNED_DESCRIPTOR(descriptor)) {
        logMsg("%s: Descriptor not aligned %x\n", (int) __FUNCTION__,
               descriptor, 3, 4, 5, 6);
	goto errorAndCleanTXD;
    }
    PETH_DESC_POSTPOP(descriptor);
    
    DRV_LOG(DRV_DEBUG_TX, "%s: new descriptor = %x\n", (int) __FUNCTION__,
            descriptor, 3, 4, 5, 6);

    /* Sanity check */
    if (!PETH_VALID_DESCRIPTOR(descriptor)) {
        logMsg("Bad descriptor %x, dropping outgoing packet\n", descriptor, 2,
               3, 4, 5, 6);
        goto errorAndCleanTXD;
    }
    
    /* Copy the data into SDRAM */
    pData = (char *) PETH_DESC_GET_PDATA(descriptor);
    DRV_LOG(DRV_DEBUG_TX, "%s: pData = %x\n", (int) __FUNCTION__,(int) pData,
            3, 4, 5, 6);

    len = netMblkToBufCopy(pMblk, pData, NULL);

#ifdef PETH_ENDIAN_HACK
    for (int i=0; i < len; i+=4) {
        *((UINT32 *)(pData + i)) = htonl(*((UINT32 *)(pData+i)));
    }
#endif PETH_ENDIAN_HACK

    /* Update the new descriptor value by writing the ingress port
       as well as the length */
    descFlags = (pDrvCtrl->unit & 0xf) << PETH_DESCFLGS_INPORT_SHIFT;
    descFlags |= (((len & 0x3f) - 1) & 0x3f) << PETH_DESCFLGS_BYTES_SHIFT;
    descFlags |= (len >> 6) + (len & 0x3ff ? 1 : 0);
    DRV_LOG(DRV_DEBUG_TX, "%s: len = %d, descFlags = %x\n", (int) __FUNCTION__,
            len, descFlags, 4, 5, 6);

    /* Now write the next link and flags into SRAM, the next link
       is NULL (i.e., 0) */
    PETH_DESC_SET_NEXT(descriptor, 0);
    PETH_DESC_SET_FLAGS(descriptor, descFlags);
 
    /* Lock down the queue head/tail and count values */
    queue = PETH_GET_QUEUE(pDrvCtrl->unit);
    if (-1 == sramUnit->read_lock_spin(queue, &headTail)) {
        logMsg("%s: failed to obtain lock, dropping packet\n", 
               (int) __FUNCTION__, 2, 3, 4, 5, 6);
        pethFreeDescriptor(descriptor);
        goto errorAndCleanTXD;
    }

    DRV_LOG(DRV_DEBUG_TX, "%s: queue = %x, headTail = %x\n",
            (int) __FUNCTION__, queue, headTail, 4, 5, 6);

    /* Read the old tail pointer */
    tail = headTail & 0xffff;
    DRV_LOG(DRV_DEBUG_TX, "%s: tail = %x\n", (int) __FUNCTION__, tail, 3, 4, 
            5, 6);

    /* Update the tail link by linking the existing tail to 
       the new descriptor, then making the new descriptor the tail.
       If there are no packets in the queue, the new descriptor becomes
       both the head and tail pointer */
    SRAM_READ(queue+1, &queueCount);
    if (0 != queueCount) {
        SRAM_WRITE(tail + SRAM_BUFF_DESCRIPTOR_BASE, descriptor);
        headTail &= 0xffff0000;
        headTail |= descriptor;
    } else {
        headTail = (descriptor << 16) | descriptor;
    }
    DRV_LOG(DRV_DEBUG_TX, "%s: new headTail = %x\n", (int) __FUNCTION__,
            headTail, 3, 4, 5, 6);

    /* Update the count */
    SRAM_WRITE(queue+1, queueCount+1);
    DRV_LOG(DRV_DEBUG_TX, "%s: queueCount = %d\n", (int) __FUNCTION__,
            queueCount, 3, 4, 5, 6);
    sramUnit->write_unlock(queue, headTail);

    /* Write the Ports with Packets bitmask to trigger the tx_arbiter
       in the microengines to send */
    if (0 == queueCount) {
        sramUnit->set_bits((XMIT_PWP_VECTOR<<2), 1 << pDrvCtrl->unit);
    }

    netMblkClChainFree(pMblk); 
    return (OK);

 errorAndCleanTXD:
    netMblkClChainFree(pMblk);
    return (ERROR);
}


/**************************************************************************
 * Function: pethIoctl()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure for this
 *                  instance of the driver
 *     cmd        - The ioctl() command to perform (see description)
 *     data       - The data (possibly) associated with cmd
 *
 * Description:
 *     This is the implementatation of this driver's ioctl.  The following
 *     is a list of the cmds accepted, and their responses and actions.
 * 
 *     Ioctl Command	PETH response/action
 *     -------------    --------------------
 *     EIOCSADDR	Sets the MAC address.  While this will properly take 
 *                      effect on future transmissions, one must be sure to 
 *                      properly update the locally bound routes in the 
 *                      microengines to deliver packets with the proper MAC 
 *                      address.  If this is not done, incoming packets will 
 *                      be dropped by the kernel.
 *     EIOCGADDR	Returns the current MAC address
 *     EIOCSFLAGS	Sets the driver flags, but ignores the value they 
 *                      are being set to.  PETH is by default in promiscuous
 *                      mode and does not care what the kernel sets the flags 
 *                      to.
 *     EIOCGFLAGS	Returns the driver flags.  These will be in sync with 
 *                      what was set, however may not reflect what the driver 
 *                      is truly doing (see EIOCSFLAGS).
 *     EIOPOLLSTART	Not implemented
 *     EIOCPOLLSTOP	Not implemented
 *     EIOCGMIB2	Returns the MIB data maintained by PETH.  This data
 *                      is updated on send and receive, but that is all.  
 *                      Error conditions etc. and not kept up-to-date.
 *     EIOCGFBUF	Returns 1536.  This value is hardcoded.
 *     EIOCGMWIDTH	Returns 1.  This value is hardcoded.
 *     EIOCGHDRLEN	Returns 14 (the size of the Ethernet header).  This
 *                      value is hardcoded.    
 *
 * Returns:
 *     EINVAL for an unknown command or other error, or 0 upon success.
 *
 * Context:
 *
 * Comments:
 *     You will find that this driver is weak in the area of ioctl's
 */
LOCAL int pethIoctl(PETHEND_DEVICE *pDrvCtrl, int cmd, caddr_t data) {
    int error = 0;
    long value;
    
    switch (cmd) {
    case EIOCSADDR: /* Set MAC address */
        DRV_LOG(DRV_DEBUG_IOCTL, "Ioctl cmd EIOCSADDR\n", 1, 2, 3, 4, 5, 6);
        if (NULL == data) {
            return (EINVAL);
        }
        bcopy ((char *)data, (char *)END_HADDR(&pDrvCtrl->end),
               END_HADDR_LEN(&pDrvCtrl->end));
        break;
        
    case EIOCGADDR: /* Get MAC address */
        DRV_LOG(DRV_DEBUG_IOCTL, 
                "Ioctl cmd EIOCGADDR: returning %x:%x:%x:%x:%x:%x\n", 
                ((char *)END_HADDR(&pDrvCtrl->end))[0],
                ((char *)END_HADDR(&pDrvCtrl->end))[1],
                ((char *)END_HADDR(&pDrvCtrl->end))[2],
                ((char *)END_HADDR(&pDrvCtrl->end))[3],
                ((char *)END_HADDR(&pDrvCtrl->end))[4],
                ((char *)END_HADDR(&pDrvCtrl->end))[5]);
        if (NULL == data) {
            return (EINVAL);
        }
        bcopy ((char *)END_HADDR(&pDrvCtrl->end), (char *)data,
               END_HADDR_LEN(&pDrvCtrl->end));
        break;
        
    case EIOCSFLAGS: /* Set driver flags - ignored, but saved */
        DRV_LOG(DRV_DEBUG_IOCTL, "Ioctl cmd EIOCSFLAGS: cur flags = 0x%x, "
                " new flags = 0x%x\n", pDrvCtrl->end.flags, (long)data, 3, 4, 
                5, 6);
        value = (long)data;
        if (value < 0) {
            value = -value;
            value--; 
            END_FLAGS_CLR (&pDrvCtrl->end, value);
        }
        else {
            END_FLAGS_SET (&pDrvCtrl->end, value);
        }
        pethConfig (pDrvCtrl);
        break;

    case EIOCGFLAGS: /* Get driver flags - may not correspond to reality */
        DRV_LOG(DRV_DEBUG_IOCTL, "Ioctl cmd EIOCGFLAGS\n", 1, 2, 3, 4, 5, 6);
        *(int *)data = END_FLAGS_GET(&pDrvCtrl->end);
        break;
        
    case EIOCPOLLSTART: /* Place the driver into polled mode - does not work */
        DRV_LOG(DRV_DEBUG_IOCTL, "Ioctl cmd EIOCPOLLSTART\n", 1, 2, 3, 4, 5, 
                6);
        error = EINVAL;
        break;
        
    case EIOCPOLLSTOP: /* Take the driver out of polled mode - does not work */
        DRV_LOG(DRV_DEBUG_IOCTL, "Ioctl cmd EIOCPOLLSTOP\n", 1, 2, 3, 4, 5, 6);
        error = EINVAL;
        break;
        
    case EIOCGMIB2: /* Return the MIB2 data structure */
        DRV_LOG(DRV_DEBUG_IOCTL, "Ioctl cmd EIOCGMIB2\n", 1, 2, 3, 4, 5, 6);
        if (NULL == data) {
            return (EINVAL);
        }
        bcopy((char *)&pDrvCtrl->end.mib2Tbl, (char *)data,
              sizeof(pDrvCtrl->end.mib2Tbl));
        break;

    case EIOCGFBUF: /* Get the maximum buffer size */
        DRV_LOG(DRV_DEBUG_IOCTL, "Ioctl cmd EIOCGFBUF\n", 1, 2, 3, 4, 5, 6);
        if (NULL == data) {
            return (EINVAL);
        }
        *(int *)data = 1536;
        break;

    case EIOCGMWIDTH: /* Get the memory width in bytes */
        DRV_LOG(DRV_DEBUG_IOCTL, "Ioctl cmd EIOCGMWIDTH\n", 1, 2, 3, 4, 5, 6);
        if (NULL == data) {
            return (EINVAL);
        }
        *(int *)data = 1;
        break;

    case EIOCGHDRLEN: /* Get the L2 header length in bytes */
        DRV_LOG(DRV_DEBUG_IOCTL, "Ioctl cmd EIOCGHDRLEN\n", 1, 2, 3, 4, 5, 6);
        if (NULL == data) {
            return (EINVAL);
        }
        *(int *)data = 14;
        break;

    default:
        DRV_LOG(DRV_DEBUG_IOCTL, "Ioctl cmd %d\n", cmd, 2, 3, 4, 5, 6);
        error = EINVAL;
    }
    return (error);
}


/**************************************************************************
 * Function: pethStart()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure for this
 *                  instance of the driver
 *
 * Description:
 *     This routine starts the polling task associated with the
 *     driver.  Only one task will be spawned at a time, regardless of how
 *     many times the routine is called.  This routine also sets
 *     the enabled flag for the driver.  This is used by pethPoller to
 *     determine if packets should be allowed in.  It is also used by
 *     pethStop to decide when to delete the polling task.
 *
 * Returns:
 *     OK if the polling task is succesfully running (not necessarily
 *     that the current call spawned the task), ERROR otherwise.
 *
 * Context:
 *
 * Comments:
 */
LOCAL STATUS pethStart(PETHEND_DEVICE* pDrvCtrl) {
    DRV_LOG(DRV_DEBUG_POLL, "%s called\n", (int) __FUNCTION__, 2, 3, 4, 5, 6);

    pDrvCtrl->enabled = 1;
    if (0 == pethPollerTID) {
        pethPollerTID = taskSpawn(PETH_RCV_POLL_TASK_NAME,
                                  PETH_RCV_POLL_PRIORITY,
                                  0, /* flags */
                                  PETH_RCV_POLL_STACK_SIZE,
                                  (FUNCPTR) pethPoller,
                                  1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    }

    return (0 == pethPollerTID) ? ERROR : OK;
}


/**************************************************************************
 * Function: pethStop()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure for this
 *                  instance of the driver
 *
 * Description:
 *     This routine stops the polling task associated with the
 *     driver if no other drivers are active.  That is, this routine
 *     only removes the polling task if no instances of the driver
 *     need it (i.e., no other instances are enabled.  Regardless of the 
 *     task, it also sets the enabled flag in the driver control structure 
 *     to indicate that the driver should not be used.
 *
 * Returns:
 *     ERROR if and only if the polling task was tried, but failed, to
 *     be removed.  OK otherwise (including that case that the driver
 *     was simply disabled and did not try and
 *     delete the polling task).
 *
 * Context:
 *
 * Comments:
 */
LOCAL STATUS pethStop(PETHEND_DEVICE* pDrvCtrl) {
    STATUS err = OK;
    int i;

    DRV_LOG(DRV_DEBUG_POLL, "%s called\n", (int) __FUNCTION__, 2, 3, 4, 5, 6);

    pDrvCtrl->enabled = 0;

    if ((!pethIsAnyDriverActive()) && (0 != pethPollerTID)) {
        err = taskDelete(pethPollerTID);
        pethPollerTID = 0;
    }
    return err;
}


/**************************************************************************
 * Function: pethPoller()
 *
 * Parameters:
 *     N/A
 *
 * Description:
 *     This routine implements the receive-side polling and demultiplexing
 *     operations.  It continually polls the core input queue for new
 *     packets.  When one arrives, it dequeues the descriptor from the
 *     queue, finds what ingress port it arrived on, and schedules
 *     the appropriate pethRecv() routine to run with the dequeued
 *     descriptor.
 *
 * Returns:
 *     N/A
 * Context:
 *
 * Comments:
 */
LOCAL void pethPoller(void) {
    UINT32 queueOffset     = PETH_GET_QUEUE(PETH_CORE_PORT);
    SramUnit *pethSramUnit = SRAM_Attach();

    /* Sanity check */
    if (NULL == pethSramUnit) {
        printf("%s: could not attach to the sram unit\n", __FUNCTION__,
               2, 3, 4, 5, 6);
        return;
    }

    while (1) {
        UINT32 queueCount;
        UINT32 headTail, head, tail;
        UINT32 nextDescriptor, descFlags;
        UINT16 ingressPort;
        PETHEND_DEVICE *pEnd;

        /* Make sure that the poller will not be deleted while
           holding critical resources */
        taskSafe();

        /* Lock down the queue so that we can dequeue */
        if (-1 == pethSramUnit->read_lock_spin(queueOffset, &headTail)) {
            logMsg("%s: failed to obtain lock, dropping packet\n", 
                   (int) __FUNCTION__, 2, 3, 4, 5, 6);
            goto nextPoll;
        }
        SRAM_READ(queueOffset+1, &queueCount);

        if (!queueCount) {
            pethSramUnit->write_unlock(queueOffset, headTail);
        } else { /* There is a packet on the queue, so dequeue it */
            DRV_LOG(DRV_DEBUG_POLL, "%s: queueCount = %d, headTail = %x\n", 
                    (int) __FUNCTION__, queueCount, headTail, 4, 5, 6);

            /* Get the queue head and tail pointers */
            head = (headTail & 0xffff0000) >> 16;
            tail = headTail & 0xffff;
            DRV_LOG(DRV_DEBUG_POLL, "%s: head= %x, tail= %x\n", 
                    (int) __FUNCTION__, head, tail, 4, 5, 6);

            /* Sanity check */
            if (!PETH_VALID_DESCRIPTOR(head)) {
                logMsg("%s: Bad descriptor, queue may be corrupt %x\n", 
                       (int) __FUNCTION__, head, 3, 4, 5, 6);
                pethSramUnit->write_unlock(queueOffset, headTail);
                return;
            }

            /* Get the next descriptor */
            PETH_DESC_GET_NEXT(head, nextDescriptor);
            PETH_DESC_GET_FLAGS(head, descFlags);
            DRV_LOG(DRV_DEBUG_POLL, "%s: nextDesc = %x, descFlags = %x\n", 
                    (int) __FUNCTION__, nextDescriptor, descFlags, 4, 5, 6);

            /* Now we have the packet, so modify the queue and release
               the lock */

            /* First, if this is the last queue, update both the head
               and the tail */
            if (0 == nextDescriptor) {
                tail = nextDescriptor;
            }
            DRV_LOG(DRV_DEBUG_POLL, "%s: rewriting headTail: %x\n",
                    (int)__FUNCTION__, (nextDescriptor << 16) | tail, 3, 4, 
                    5, 6);

            /* Update the number of packets on the queue */
            SRAM_WRITE(queueOffset+1, queueCount-1);

            pethSramUnit->write_unlock(queueOffset, 
                                       (nextDescriptor << 16) | tail);
            DRV_LOG(DRV_DEBUG_POLL, "%s: unlocked queue\n",
                    (int) __FUNCTION__, 2, 3, 4, 5, 6);

            /* Now we have dequeued the descriptor, so give it to the driver */
            ingressPort = PETH_DESCFLGS_GET_INPORT(descFlags);
            DRV_LOG(DRV_DEBUG_POLL, "%s: ingressPort = %d\n",
                    (int) __FUNCTION__, ingressPort, 3, 4, 5, 6);

            if (ingressPort >= PETH_NUM_DEVS) {
                logMsg("%s: failed to find driver (%d), discarding packet\n",
                       (int) __FUNCTION__, ingressPort, 3, 4, 5, 6);
                pethFreeDescriptor(head);
                goto nextPoll;
            }

            /* Get the driver control structure and only schedule the
               packet for pethRecv if the driver is enabled */
            pEnd = pethDevices[ingressPort];
            if ((NULL == pEnd) || (!pEnd->enabled)) {
                logMsg("%s: packet received for disabled interface: %d\n",
                       (int) __FUNCTION__, ingressPort, 3, 4, 5, 6);
                pethFreeDescriptor(head);
                goto nextPoll;
            }
            
            if (OK != 
                netJobAdd((FUNCPTR)pethRecv, (int)pEnd, (int)head, 3, 4 ,5)) {
                logMsg("%s: failed to schedule pethRecv, dropping packet\n",
                       (int) __FUNCTION__, 2, 3, 4, 5, 6);
                pethFreeDescriptor(head);
            }
        }
    nextPoll:
        /* Allow the task to be deleted during our pause */
        taskUnsafe();
        if (queueCount <= 1) {
            taskDelay(PETH_RCV_POLL_RATE);
	}
    }
}


/**************************************************************************
 * Function: pethFreeDescriptor()
 *
 * Parameters:
 *     descriptor   - The descriptor to free
 *
 * Description:
 *     Frees the given descriptor by pushing it back onto the SRAM
 *     freelist.  The descriptor is validated before being pushed
 *     back.
 *
 * Returns:
 *     OK if the descriptor was free'd, ERROR for any other problem
 *
 * Context:
 *
 * Comments:
 */
LOCAL STATUS pethFreeDescriptor(unsigned int descriptor) {
    DRV_LOG(DRV_DEBUG_RX | DRV_DEBUG_TX, "%s: descriptor = %x\n",
            (int) __FUNCTION__, descriptor, 3, 4, 5, 6);

    SramUnit *sram = SRAM_Attach();
    if (NULL == sram) {
        logMsg("%s: could not free memory because SRAM_Attach failed\n",
               (int) __FUNCTION__, 2, 3, 4, 5, 6);
        return ERROR;
    }

    /* Sanity checks to ensure the descriptor is valid */
    if (!PETH_VALID_DESCRIPTOR(descriptor)) {
        logMsg("%s: Bad descriptor %x\n", (int) __FUNCTION__, descriptor,
               3, 4, 5, 6);
        return ERROR;
    }

    PETH_DESC_PREPUSH(descriptor);
    if (!PETH_ALIGNED_DESCRIPTOR(descriptor)) {
        logMsg("%s: Descriptor not aligned %x\n",
               (int) __FUNCTION__, descriptor, 3, 4, 5, 6);
	return ERROR;
    }
    sram->push(0, ((descriptor + SRAM_BUFF_DESCRIPTOR_BASE) << 2));
    return OK;
}


/**************************************************************************
 * Function: pethIsAnyDriverActive()
 *
 * Parameters:
 *     N/A
 *
 * Description:
 *     Checks to see if any drivers are currently enabled.
 *
 * Returns:
 *     0 if no drivers are currently enabled.  Non-zero otherwise
 *
 * Context:
 *
 * Comments:
 */
LOCAL int pethIsAnyDriverActive() {
    int i;

    for (i = 0; i < PETH_NUM_DEVS; ++i) {
        if (NULL != pethDevices[i]) {
            if (pethDevices[i]->enabled) {
                return 1;
            }
        }
    }
    return 0;
}


/****************************************************************************
 ****************************************************************************
 * STUBS
 *   - All of the routines below this point in the file are stubs, 
 *     executing just enough to allow the driver to compile and
 *     register itself with the kernel
 ****************************************************************************/

/**************************************************************************
 * Function: pethMCastAddrAdd()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure associated
 *                  with this instance of the driver
 *     pAddress   - The new multicast address to listen for
 *
 * Description:
 *     This routine adds the new multicast address to a list soley for
 *     the purpose of being able to respond to the corresponding MCastGet
 *     query from the kernel.
 *
 * Returns:
 *    OK
 * Context:
 *
 * Comments:
 *     The underlying hardware is NOT affected by this call.
 */
LOCAL STATUS pethMCastAddrAdd(PETHEND_DEVICE* pDrvCtrl, char* pAddress) { 
    DRV_LOG(DRV_DEBUG_LOAD, "%s: pAddress = %x\n", (int) __FUNCTION__, 
          (int)  pAddress, 3, 4, 5, 6); 
    etherMultiAdd(&pDrvCtrl->end.multiList, pAddress);
    return OK;
}


/**************************************************************************
 * Function: pethMCastAddrDel()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure associated
 *                  with this instance of the driver
 *     pAddress   - The multicast address to stop listening for
 *
 * Description:
 *     This routine removes the multicast address from a list soley for
 *     the purpose of being able to respond to the corresponding MCastGet
 *     query from the kernel.
 *
 * Returns:
 *    OK
 * Context:
 *
 * Comments:
 *     The underlying hardware is NOT affected by this call.
 */
LOCAL STATUS  pethMCastAddrDel(PETHEND_DEVICE* pDrvCtrl, char* pAddress) { 
/*    DRV_LOG(DRV_DEBUG_LOAD, "%s: pAddress = %x\n", (int) __FUNCTION__, 
            pAddress, 3, 4, 5, 6); */
    etherMultiDel(&pDrvCtrl->end.multiList, pAddress);
    return OK;
}


/**************************************************************************
 * Function: pethMCastAddrGet()
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure associated
 *                  with this instance of the driver
 *     pTable     - A pointer to a table to fill in with the multicast
 *                  addresses that we are currently listening to
 *
 * Description:
 *     This routine returns the list of multicast address that this
 *     driver has been instructed to listen to, but the reality is that
 *     the hardware is unaffected by this list.
 *
 * Returns:
 *     The status of the call to etherMultiGet()
 *
 * Context:
 *
 * Comments:
 *     The underlying hardware is NOT affected by this call.
 */
LOCAL STATUS  pethMCastAddrGet(PETHEND_DEVICE* pDrvCtrl, MULTI_TABLE* pTable) {
    DRV_LOG(DRV_DEBUG_LOAD, "%s\n", (int) __FUNCTION__, 2, 3, 4, 5, 6); 
    return etherMultiGet(&pDrvCtrl->end.multiList, pTable);
}


/**************************************************************************
 * Function: pethConfig() - STUB
 *
 * Parameters:
 *     pDrvCtrl    - A pointer to the driver control structure for this
 *                   instance of the driver
 *
 * Description:
 *     This routine gets called each time the driver's flags are
 *     are modified (see pethIoctl()).  Since the driver currently
 *     ignores these changes, the routine is stubbed out.  The
 *     routine is not completely removed in anticipation of future
 *     development.
 *
 * Returns:
 *     N/A
 * Context:
 *
 * Comments:
 */
LOCAL void pethConfig(PETHEND_DEVICE *pDrvCtrl) {
    DRV_LOG(DRV_DEBUG_LOAD, "%s\n", (int) __FUNCTION__, 2, 3, 4, 5, 6);
}


/**************************************************************************
 * Function: pethPollSend() - STUB
 *
 * Parameters:
 *     pDrvCtrl    - A pointer to the driver control structure for this
 *                   instance of the driver.
 *     pBuf        - An MBLK pointer containing the packet to transmit
 *
 * Description:
 *     This routine is called by the kernel after the driver has been
 *     put into polled mode (see pethIoctl()).  Since this driver does
 *     not support polled mode (in the END sense), this routine is a
 *     stub.  This routine could, however, be implemented at it
 *     would be identical to pethSend().
 *
 * Returns:
 *     ERROR
 * Context:
 *
 * Comments:
 */
LOCAL STATUS pethPollSend(PETHEND_DEVICE* pDrvCtrl, M_BLK_ID pBuf) { 
    DRV_LOG(DRV_DEBUG_LOAD, "%s\n", (int) __FUNCTION__, 2, 3, 4, 5, 6); 
    return ERROR;
}


/**************************************************************************
 * Function: pethPollReceive() - STUB
 *
 * Parameters:
 *     pDrvCtrl   - A pointer to the driver control structure for this
 *                  instance of the driver
 *     pBuf       - An MBLK pointer where the new packet should  be
 *                  placed.
 *
 * Description:
 *     This routine is called by the kernel after the driver has been
 *     set into polled mode (see pethIoctl()).  Since this driver does
 *     not support polled mode (in the END sense), this routine is a
 *     stub.  It could, however, be implemented, in which case it
 *     would look just like pethRecv and pethPoller put together.  
 *     The ideal solution would be to
 *     factor out pethRecv and use the common code for both routines.
 *
 * Returns:
 *     ERROR
 * Context:
 *
 * Comments:
 */
LOCAL STATUS pethPollReceive(PETHEND_DEVICE* pDrvCtrl, M_BLK_ID pBuf) { 
    DRV_LOG(DRV_DEBUG_LOAD, "%s\n", (int) __FUNCTION__, 2, 3, 4, 5, 6); 
    return ERROR;
}

} /* extern "C" */


