summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/mips/csb350/network/network.c
blob: a9dc5df28185efead3117478e374a6c240e8ab31 (plain) (tree)
1
2
3
4
5
6
7
8
9


         
                          


   

                                                 
  
                                                           
                                                        
                                         




                               
                        
                          
                    












































                                                                   
 



                                           
 



                                                    
 




                                                  
 















                                                             



                                            


























                                                  
 





                                                
                                             


                         
                                                                   








                                                                
                                                                                        

                                                                          

                                              
                                                                         







                                                                       
                                                              

                                                                
 





                                                                       
                                                                         








                                                                       
 








                                                                       
                  






















                                                             
 




                                                                              
 


















                                                  
 
                                       
 
















                                                                            
                                                                              






                                                                              
                                                                             
                                         
                                                                             









                                                          
 







                                                    
 

























                                           
                                      

                                          


                                                                    




                                  

 

                                           






                                                       
                






                                                       


                                                        
 

                                                       
 



                                                        
 




                                                          


                                                                       
                                                     



                                                                       
                                                     
 




                                                                             
 



                                         
 
      
                   
       
                                                              

                                                                 
 









                                                  
                       












                                        


                                     





































                                                                                
 









                                                               
                    
           


                                                 
         
                                                               



























                                                                      
 
                                                               





                                               
 
                                  
 




                                         
                                                               












                                                          
                                                    











































                                                         
                    









                                                
                                                                           










































                                                                             

                                                     





                                                             
 


                                                                                   
 
                                           
 



                                                                 
 




                                                                 
 

                                        
 

                                                                       
                            
                   
                 

                                                          
                 
 





                                                     
 
                                                                  
                                                                             




                                                                     
 















                                                                   
                   





                                                                             
 


                                                      
                                              

                     
 
                                                                           
                                                               
                                                                      
 
                                                                
                                                                        










                                                                   
                                                           



                                                              
      


















                                                           
 




















                                                              
 









                                                      
                                                                            


                                            
 




                                         
 





                                                       
 


                                  
 



                                  
 



                                 
 


                               
 












                                                                        
                                     


                    


                                   




                     
                                                              







                                                                    
 





                                             
                                                                   



                                                                     
                        

                            
 









                                                
 








                                             
                                                                   


     
/**
 *  @file
 *  
 *  Au1x00 ethernet driver
 */

/*
 *  Copyright (c) 2005 by Cogent Computer Systems
 *  Written by Jay Monkman <jtm@lopingdog.com>
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 */

#include <rtems.h>
#include <rtems/rtems_bsdnet.h>
#include <bsp.h>
#include <rtems/bspIo.h>
#include <libcpu/au1x00.h>
#include <bsp/irq.h>

#include <stdio.h>
#include <string.h>

#include <errno.h>
#include <rtems/error.h>

#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sockio.h>

#include <net/if.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>

#include <assert.h>

#define NUM_IFACES 1
#define NUM_TX_DMA_BUFS 4
#define NUM_RX_DMA_BUFS 4

/*  RTEMS event used to start tx daemon. */
#define START_TX_EVENT  RTEMS_EVENT_1
/*  RTEMS event used to start rx daemon. */
#define START_RX_EVENT  RTEMS_EVENT_2

rtems_isr au1x00_emac_isr(rtems_vector_number vector);

#define TX_BUF_SIZE 2048

char tx_buf_base[(4 * TX_BUF_SIZE) + 32];

volatile int wait_count;
/*
 * Hardware-specific storage
 */
typedef struct
{
    /*
     * Connection to networking code
     * This entry *must* be the first in the sonic_softc structure.
     */
    struct arpcom                    arpcom;

    /*
     * Interrupt vector
     */
    rtems_vector_number             vector;

    /*
     *  Indicates configuration
     */
    int                             acceptBroadcast;

    /*
     * Tasks waiting for interrupts
     */
    rtems_id                        rx_daemon_tid;
    rtems_id                        tx_daemon_tid;

    /*
     * Buffers
     */
    au1x00_macdma_rx_t             *rx_dma;
    au1x00_macdma_tx_t             *tx_dma;
    int                             rx_head;
    int                             rx_tail;
    int                             tx_head;
    int                             tx_tail;
    struct mbuf                    *rx_mbuf[NUM_RX_DMA_BUFS];

    unsigned char                  *tx_buf[4];

    /*
     * register addresses
     */
    uint32_t                      ctrl_regs;
    uint32_t                     *en_reg;
    uint32_t                      int_mask;
    uint32_t                      int_ctrlr;

    /*
     * device
     */
    int                             unitnumber;

    /*
     * Statistics
     */
    unsigned long                   interrupts;
    unsigned long                   rx_interrupts;
    unsigned long                   tx_interrupts;
    unsigned long                   rx_missed;
    unsigned long                   rx_bcast;
    unsigned long                   rx_mcast;
    unsigned long                   rx_unsupp;
    unsigned long                   rx_ctrl;
    unsigned long                   rx_len_err;
    unsigned long                   rx_crc_err;
    unsigned long                   rx_dribble;
    unsigned long                   rx_mii_err;
    unsigned long                   rx_collision;
    unsigned long                   rx_too_long;
    unsigned long                   rx_runt;
    unsigned long                   rx_watchdog;
    unsigned long                   rx_pkts;
    unsigned long                   rx_dropped;

    unsigned long                   tx_deferred;
    unsigned long                   tx_underrun;
    unsigned long                   tx_aborted;
    unsigned long                   tx_pkts;
} au1x00_emac_softc_t;

static au1x00_emac_softc_t softc[NUM_IFACES];


/* function prototypes */
int rtems_au1x00_emac_attach (struct rtems_bsdnet_ifconfig *config,
                              int attaching);
void au1x00_emac_init(void *arg);
void au1x00_emac_init_hw(au1x00_emac_softc_t *sc);
void au1x00_emac_start(struct ifnet *ifp);
void au1x00_emac_stop (au1x00_emac_softc_t *sc);
void au1x00_emac_tx_daemon (void *arg);
void au1x00_emac_rx_daemon (void *arg);
void au1x00_emac_sendpacket (struct ifnet *ifp, struct mbuf *m);
void au1x00_emac_stats (au1x00_emac_softc_t *sc);
static int au1x00_emac_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data);
static void mii_write(au1x00_emac_softc_t *sc, uint8_t reg, uint16_t val);
static void mii_read(au1x00_emac_softc_t *sc, uint8_t reg, uint16_t *val);
static void mii_init(au1x00_emac_softc_t *sc);

static void mii_write(au1x00_emac_softc_t *sc, uint8_t reg, uint16_t val)
{
    /* wait for the interface to get unbusy */
    while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) {
        continue;
    }

    /* write to address 0 - we only support address 0 */
    AU1X00_MAC_MIIDATA(sc->ctrl_regs) = val;
    AU1X00_MAC_MIICTRL(sc->ctrl_regs) = (((reg & 0x1f) << 6) |
                                         AU1X00_MAC_MIICTRL_MW);
    au_sync();

    /* wait for it to complete */
    while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) {
        continue;
    }
}

static void mii_read(au1x00_emac_softc_t *sc, uint8_t reg, uint16_t *val)
{
    /* wait for the interface to get unbusy */
    while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) {
        continue;
    }

    /* write to address 0 - we only support address 0 */
    AU1X00_MAC_MIICTRL(sc->ctrl_regs) = ((reg & 0x1f) << 6);
    au_sync();

    /* wait for it to complete */
    while (AU1X00_MAC_MIICTRL(sc->ctrl_regs) & AU1X00_MAC_MIICTRL_MB) {
        continue;
    }
    *val = AU1X00_MAC_MIIDATA(sc->ctrl_regs);
}

static void mii_init(au1x00_emac_softc_t *sc)
{
    uint16_t data;

    mii_write(sc, 0, 0x8000);     /* reset */
    do {
        mii_read(sc, 0, &data);
    } while (data & 0x8000);

    mii_write(sc, 0, 0x3200);     /* reset autonegotiation */
    mii_write(sc, 17, 0xffc0);    /* setup LEDs */

}



int rtems_au1x00_emac_attach (
    struct rtems_bsdnet_ifconfig *config,
    int attaching
    )
{
    struct ifnet *ifp;
    int mtu;
    int unitnumber;
    char *unitname;
    static au1x00_emac_softc_t *sc;

    /*
     * Parse driver name
     */
    if ((unitnumber = rtems_bsdnet_parse_driver_name (config, &unitname)) < 0)
        return 0;

    /*
     * Is driver free?
     */
    if (unitnumber > NUM_IFACES) {
        printf ("Bad AU1X00 EMAC unit number.\n");
        return 0;
    }

    sc = &softc[unitnumber];

    ifp = &sc->arpcom.ac_if;
    if (ifp->if_softc != NULL) {
        printf ("Driver already in use.\n");
        return 0;
    }

    /*
     *  zero out the control structure
     */

    memset((void *)sc, 0, sizeof(*sc));

    sc->unitnumber = unitnumber;
    sc->int_ctrlr = AU1X00_IC0_ADDR;

    if (unitnumber == 0) {
        sc->ctrl_regs = AU1100_MAC0_ADDR;
        sc->en_reg = (void *)(AU1100_MACEN_ADDR + 0);

        sc->tx_dma = (void *)(AU1X00_MACDMA0_ADDR + 0x000);
        sc->rx_dma = (void *)(AU1X00_MACDMA0_ADDR + 0x100);
        sc->int_mask = AU1X00_IC_IRQ_MAC0;
    } else {
        printk("Unknown network device: %d\n", unitnumber);
        return 0;
    }

    /* If the ethernet controller is already set up, read the MAC address */
    if ((*sc->en_reg & 0x33) == 0x33) {
        sc->arpcom.ac_enaddr[5] = ((AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 8) &
                                   0xff);
        sc->arpcom.ac_enaddr[4] = ((AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 0) &
                                   0xff);
        sc->arpcom.ac_enaddr[3] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 24) &
                                   0xff);
        sc->arpcom.ac_enaddr[2] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 16) &
                                   0xff);
        sc->arpcom.ac_enaddr[1] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 8) &
                                   0xff);
        sc->arpcom.ac_enaddr[0] = ((AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 0) &
                                   0xff);
    } else {
        /* It's not set up yet, so we set a MAC address */
        sc->arpcom.ac_enaddr[5] = 0x05;
        sc->arpcom.ac_enaddr[4] = 0xc0;
        sc->arpcom.ac_enaddr[3] = 0x50;
        sc->arpcom.ac_enaddr[2] = 0x31;
        sc->arpcom.ac_enaddr[1] = 0x23;
        sc->arpcom.ac_enaddr[0] = 0x00;
    }


    if (config->mtu) {
        mtu = config->mtu;
    } else {
        mtu = ETHERMTU;
    }

    sc->acceptBroadcast = !config->ignore_broadcast;

    /*
     * Set up network interface values
     */
    ifp->if_softc = sc;
    ifp->if_unit = unitnumber;
    ifp->if_name = unitname;
    ifp->if_mtu = mtu;
    ifp->if_init = au1x00_emac_init;
    ifp->if_ioctl = au1x00_emac_ioctl;
    ifp->if_start = au1x00_emac_start;
    ifp->if_output = ether_output;
    ifp->if_flags = IFF_BROADCAST;
    if (ifp->if_snd.ifq_maxlen == 0) {
        ifp->if_snd.ifq_maxlen = ifqmaxlen;
    }

    /*
     * Attach the interface
     */
    if_attach (ifp);
    ether_ifattach (ifp);
    return 1;
}

void au1x00_emac_init(void *arg)
{
    au1x00_emac_softc_t     *sc = arg;
    struct ifnet *ifp = &sc->arpcom.ac_if;

    /*
     *This is for stuff that only gets done once (au1x00_emac_init()
     * gets called multiple times
     */
    if (sc->tx_daemon_tid == 0)
    {
        /* Set up EMAC hardware */
        au1x00_emac_init_hw(sc);


        /* install the interrupt handler */
        if (sc->unitnumber == 0) {
            rtems_interrupt_handler_install(
              AU1X00_IRQ_MAC0,
              "NIC0",
              0,
              (rtems_interrupt_handler)au1x00_emac_isr,
              NULL
            );
        } else {
            rtems_interrupt_handler_install(
              AU1X00_IRQ_MAC1,
              "NIC1",
              0,
              (rtems_interrupt_handler)au1x00_emac_isr,
              NULL
            );
        }
        AU1X00_IC_MASKCLR(sc->int_ctrlr) = sc->int_mask;
        au_sync();

        /* set src bit */
        AU1X00_IC_SRCSET(sc->int_ctrlr) = sc->int_mask;

        /* high level */
        AU1X00_IC_CFG0SET(sc->int_ctrlr) = sc->int_mask;
        AU1X00_IC_CFG1CLR(sc->int_ctrlr) = sc->int_mask;
        AU1X00_IC_CFG2SET(sc->int_ctrlr) = sc->int_mask;

        /* assign to request 0 - negative logic */
        AU1X00_IC_ASSIGNSET(sc->int_ctrlr) = sc->int_mask;
        au_sync();

        /* Start driver tasks */
        sc->tx_daemon_tid = rtems_bsdnet_newproc("ENTx",
                                                 4096,
                                                 au1x00_emac_tx_daemon,
                                                 sc);

        sc->rx_daemon_tid = rtems_bsdnet_newproc("ENRx",
                                                 4096,
                                                 au1x00_emac_rx_daemon,
                                                 sc);


    }
    /* EMAC doesn't support promiscuous, so ignore requests */
    if (ifp->if_flags & IFF_PROMISC)
        printf ("Warning - AU1X00 EMAC doesn't support Promiscuous Mode!\n");

    /*
     * Tell the world that we're running.
     */
    ifp->if_flags |= IFF_RUNNING;

    /*
     * start tx, rx
     */
    AU1X00_MAC_CONTROL(sc->ctrl_regs) |= (AU1X00_MAC_CTRL_TE |
                                             AU1X00_MAC_CTRL_RE);
    au_sync();


} /* au1x00_emac_init() */

void  au1x00_emac_init_hw(au1x00_emac_softc_t *sc)
{
    int i;
    struct mbuf *m;
    struct ifnet *ifp = &sc->arpcom.ac_if;

    /* reset the MAC */
    *sc->en_reg = 0x40;
    au_sync();
    for (i = 0; i < 10000; i++) {
        continue;
    }

/*    *sc->en_reg = AU1X00_MAC_EN_CE; */
    *sc->en_reg = 41;
    au_sync();
    for (i = 0; i < 10000; i++) {
        continue;
    }

/*
    *sc->en_reg = (AU1X00_MAC_EN_CE |
                   AU1X00_MAC_EN_E2 |
                   AU1X00_MAC_EN_E1 |
                   AU1X00_MAC_EN_E0);
*/
    *sc->en_reg = 0x33;
    au_sync();
    mii_init(sc);

    /* set the mac address */
    AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) = ((sc->arpcom.ac_enaddr[5] << 8) |
                                          (sc->arpcom.ac_enaddr[4] << 0));
    AU1X00_MAC_ADDRLOW(sc->ctrl_regs) = ((sc->arpcom.ac_enaddr[3] << 24) |
                                         (sc->arpcom.ac_enaddr[2] << 16) |
                                         (sc->arpcom.ac_enaddr[1] << 8) |
                                         (sc->arpcom.ac_enaddr[0] << 0));


    /* get the MAC address from the chip */
    sc->arpcom.ac_enaddr[5] = (AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 8) & 0xff;
    sc->arpcom.ac_enaddr[4] = (AU1X00_MAC_ADDRHIGH(sc->ctrl_regs) >> 0) & 0xff;
    sc->arpcom.ac_enaddr[3] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 24) & 0xff;
    sc->arpcom.ac_enaddr[2] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 16) & 0xff;
    sc->arpcom.ac_enaddr[1] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 8) & 0xff;
    sc->arpcom.ac_enaddr[0] = (AU1X00_MAC_ADDRLOW(sc->ctrl_regs) >> 0) & 0xff;

    printk("Setting mac_control to 0x%x\n",
           (AU1X00_MAC_CTRL_F |
            AU1X00_MAC_CTRL_PM |
            AU1X00_MAC_CTRL_RA |
            AU1X00_MAC_CTRL_DO |
            AU1X00_MAC_CTRL_EM));

    AU1X00_MAC_CONTROL(sc->ctrl_regs) = (AU1X00_MAC_CTRL_F |   /* full duplex */
                                         AU1X00_MAC_CTRL_PM |  /* pass mcast */
                                         AU1X00_MAC_CTRL_RA |  /* recv all */
                                         AU1X00_MAC_CTRL_DO |  /* disable own */
                                         AU1X00_MAC_CTRL_EM);  /* Big endian */
    au_sync();
    printk("mac_control was set to 0x%x\n", AU1X00_MAC_CONTROL(sc->ctrl_regs));
    printk("mac_control addr is 0x%x\n", &AU1X00_MAC_CONTROL(sc->ctrl_regs));

    /* initialize our receive buffer descriptors */
    for (i = 0; i < NUM_RX_DMA_BUFS; i++) {
        MGETHDR(m, M_WAIT, MT_DATA);
        MCLGET(m, M_WAIT);

        m->m_pkthdr.rcvif = ifp;
        m->m_nextpkt = 0;

        /*
         * The receive buffer must be aligned with a cache line
         * boundary.
         */
        if (mtod(m, uint32_t) & 0x1f) {
	  uint32_t *p = mtod(m, uint32_t *);
          *p = (mtod(m, uint32_t) + 0x1f) & 0x1f;
        }
        sc->rx_dma[i].addr = (mtod(m, uint32_t) & ~0xe0000000);
        sc->rx_mbuf[i] = m;
    }

    /* Initialize transmit buffer descriptors */
    for (i = 0; i < NUM_TX_DMA_BUFS; i++) {
        sc->tx_dma[i].addr = 0;
    }

    /* initialize the transmit buffers */
    sc->tx_buf[0] = (void *)((((int)&tx_buf_base[0]) + 0x1f) & ~0x1f);
    sc->tx_buf[1] = (void *)(((int)sc->tx_buf[0]) + TX_BUF_SIZE);
    sc->tx_buf[2] = (void *)(((int)sc->tx_buf[1]) + TX_BUF_SIZE);
    sc->tx_buf[3] = (void *)(((int)sc->tx_buf[2]) + TX_BUF_SIZE);

    sc->rx_head = (sc->rx_dma[0].addr >> 2) & 0x3;
    sc->rx_tail = (sc->rx_dma[0].addr >> 2) & 0x3;
    sc->tx_head = (sc->tx_dma[0].addr >> 2) & 0x3;
    sc->tx_tail = (sc->tx_dma[0].addr >> 2) & 0x3;

    for (i = 0; i < NUM_RX_DMA_BUFS; i++) {
        sc->rx_dma[i].addr |= AU1X00_MAC_DMA_RXADDR_EN;
    }

} /* au1x00_emac_init_hw() */

void au1x00_emac_start(struct ifnet *ifp)
{
    au1x00_emac_softc_t *sc = ifp->if_softc;

    rtems_bsdnet_event_send(sc->tx_daemon_tid, START_TX_EVENT);
    ifp->if_flags |= IFF_OACTIVE;
}

void au1x00_emac_stop (au1x00_emac_softc_t *sc)
{
    struct ifnet *ifp = &sc->arpcom.ac_if;

    ifp->if_flags &= ~IFF_RUNNING;

    /*
     * Stop the transmitter and receiver.
     */

    /* Disable TX/RX  */
    AU1X00_MAC_CONTROL(sc->ctrl_regs) &= ~(AU1X00_MAC_CTRL_TE |
                                      AU1X00_MAC_CTRL_RE);
    au_sync();
}

/*
 * Driver tx daemon
 */
void au1x00_emac_tx_daemon (void *arg)
{
    au1x00_emac_softc_t *sc = (au1x00_emac_softc_t *)arg;
    struct ifnet *ifp = &sc->arpcom.ac_if;
    struct mbuf *m;
    rtems_event_set events;
    uint32_t ic_base;     /* interrupt controller */

    ic_base = AU1X00_IC0_ADDR;

    /* turn on interrupt, then wait for one */
    if (sc->unitnumber == 0) {
        AU1X00_IC_MASKSET(ic_base) = AU1X00_IC_IRQ_MAC0;
    } else {
        AU1X00_IC_MASKSET(ic_base) = AU1X00_IC_IRQ_MAC1;
    }
    au_sync();

    for (;;)
    {
        rtems_bsdnet_event_receive(
            START_TX_EVENT,
            RTEMS_EVENT_ANY | RTEMS_WAIT,
            RTEMS_NO_TIMEOUT,
            &events);

        /* Send packets till queue is empty */
        for (;;)
        {
            /* Get the next mbuf chain to transmit. */
            IF_DEQUEUE(&ifp->if_snd, m);
            if (!m)
                break;

            sc->tx_pkts++;
            au1x00_emac_sendpacket (ifp, m);
        }
        ifp->if_flags &= ~IFF_OACTIVE;
    }
}

/*
 * Driver rx daemon
 */
void au1x00_emac_rx_daemon (void *arg)
{
    au1x00_emac_softc_t *sc = (au1x00_emac_softc_t *)arg;
    struct ifnet *ifp = &sc->arpcom.ac_if;
    struct mbuf *m;
    struct ether_header *eh;
    rtems_event_set events;
    uint32_t status;

    while (1) {
        rtems_bsdnet_event_receive(
            START_RX_EVENT,
            RTEMS_EVENT_ANY | RTEMS_WAIT,
            RTEMS_NO_TIMEOUT,
            &events);

        /* while there are packets to receive */

        while (!(sc->rx_dma[sc->rx_head].addr & (AU1X00_MAC_DMA_RXADDR_DN |
                                                AU1X00_MAC_DMA_RXADDR_EN))) {
            status = sc->rx_dma[sc->rx_head].stat;
            if (status & AU1X00_MAC_DMA_RXSTAT_MI) {
                sc->rx_missed++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_BF) {
                sc->rx_bcast++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_MF) {
                sc->rx_mcast++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_UC) {
                sc->rx_unsupp++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_CF) {
                sc->rx_ctrl++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_LE) {
                sc->rx_len_err++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_CR) {
                sc->rx_crc_err++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_DB) {
                sc->rx_dribble++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_ME) {
                sc->rx_mii_err++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_CS) {
                sc->rx_collision++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_FL) {
                sc->rx_too_long++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_RF) {
                sc->rx_runt++;
            }
            if (status & AU1X00_MAC_DMA_RXSTAT_WT) {
                sc->rx_watchdog++;
            }

            /* If no errrors, accept packet */
            if ((status & (AU1X00_MAC_DMA_RXSTAT_CR |
                           AU1X00_MAC_DMA_RXSTAT_DB |
                           AU1X00_MAC_DMA_RXSTAT_RF)) == 0) {

                sc->rx_pkts++;

                /* find the start of the mbuf */
                m = sc->rx_mbuf[sc->rx_head];

                /* set the length of the mbuf */
                m->m_len = AU1X00_MAC_DMA_RXSTAT_LEN(sc->rx_dma[sc->rx_head].stat);
                m->m_len -= 4; /* remove ethernet CRC */

                m->m_pkthdr.len = m->m_len;

                /* strip off the ethernet header from the mbuf */
                /* but save the pointer to it */
                eh = mtod (m, struct ether_header *);
                m->m_data += sizeof(struct ether_header);

                /* give the received packet to the stack */
                ether_input(ifp, eh, m);
                /* get a new buf and make it ready for the MAC */
                MGETHDR(m, M_WAIT, MT_DATA);
                MCLGET(m, M_WAIT);

                m->m_pkthdr.rcvif = ifp;
                m->m_nextpkt = 0;

                /*
                 * The receive buffer must be aligned with a cache line
                 * boundary.
                 */
		{
                  uint32_t *p = mtod(m, uint32_t *);
                  *p = (mtod(m, uint32_t) + 0x1f) & ~0x1f;
		}

            } else {
                sc->rx_dropped++;

                /* find the mbuf so we can reuse it*/
                m = sc->rx_mbuf[sc->rx_head];
            }

            /* set up the receive dma to use the mbuf's cluster */
            sc->rx_dma[sc->rx_head].addr = (mtod(m, uint32_t) & ~0xe0000000);
            au_sync();
            sc->rx_mbuf[sc->rx_head] = m;

            sc->rx_dma[sc->rx_head].addr |= AU1X00_MAC_DMA_RXADDR_EN;
            au_sync();


            /* increment the buffer index */
            sc->rx_head++;
            if (sc->rx_head >= NUM_RX_DMA_BUFS) {
                sc->rx_head = 0;
            }
        }
    }
}

/* Send packet */
void au1x00_emac_sendpacket (struct ifnet *ifp, struct mbuf *m)
{
    struct mbuf *l = NULL;
    unsigned int pkt_offset = 0;
    au1x00_emac_softc_t *sc = (au1x00_emac_softc_t *)ifp->if_softc;
    uint32_t txbuf;

    /* Wait for EMAC Transmit Queue to become available. */
    while((sc->tx_dma[sc->tx_head].addr & (AU1X00_MAC_DMA_TXADDR_EN ||
                                           AU1X00_MAC_DMA_TXADDR_DN)) != 0) {
        continue;
    }

    /* copy the mbuf chain into the transmit buffer */
    l = m;

    txbuf = (uint32_t)sc->tx_buf[sc->tx_head];
    while (l != NULL)
    {

        memcpy(((char *)txbuf + pkt_offset), /* offset into pkt for mbuf */
               (char *)mtod(l, void *),      /* cast to void */
               l->m_len);                    /* length of this mbuf */

        pkt_offset += l->m_len;              /* update offset */
        l = l->m_next;                       /* get next mbuf, if any */
    }

    /* Pad if necessary */
    if (pkt_offset < 60) {
        memset((char *)(txbuf + pkt_offset), 0, (60 - pkt_offset));
        pkt_offset = 60;
    }

    /* send it off */
    sc->tx_dma[sc->tx_head].stat = 0;
    sc->tx_dma[sc->tx_head].len = pkt_offset;
    sc->tx_dma[sc->tx_head].addr = ((txbuf & ~0xe0000000) |
                                    AU1X00_MAC_DMA_TXADDR_EN);
    au_sync();


    /*
     *Without this delay, some outgoing packets never
     * make it out the device. Nothing in the documentation
     * explains this.
     */
    for (wait_count = 0; wait_count < 5000; wait_count++){
        continue;
    }

    /* free the mbuf chain we just copied */
    m_freem(m);

    sc->tx_head++;
    if (sc->tx_head >= NUM_TX_DMA_BUFS) {
        sc->tx_head = 0;
    }

} /* au1x00_emac_sendpacket () */



/* Show interface statistics */
void au1x00_emac_stats (au1x00_emac_softc_t *sc)
{
    printf("Interrupts:%-8lu", sc->interrupts);
    printf("    RX Interrupts:%-8lu", sc->rx_interrupts);
    printf(" TX Interrupts:%-8lu\n", sc->tx_interrupts);
    printf("RX Packets:%-8lu", sc->rx_pkts);
    printf("    RX Control:%-8lu", sc->rx_ctrl);
    printf("    RX broadcast:%-8lu\n", sc->rx_bcast);
    printf("RX Mcast:%-8lu", sc->rx_mcast);
    printf("      RX missed:%-8lu", sc->rx_missed);
    printf("     RX Unsupported ctrl:%-8lu\n", sc->rx_unsupp);
    printf("RX Len err:%-8lu", sc->rx_len_err);
    printf("    RX CRC err:%-8lu", sc->rx_crc_err);
    printf("    RX dribble:%-8lu\n", sc->rx_dribble);
    printf("RX MII err:%-8lu", sc->rx_mii_err);
    printf("    RX collision:%-8lu", sc->rx_collision);
    printf("  RX too long:%-8lu\n", sc->rx_too_long);
    printf("RX runt:%-8lu", sc->rx_runt);
    printf("       RX watchdog:%-8lu", sc->rx_watchdog);
    printf("   RX dropped:%-8lu\n", sc->rx_dropped);

    printf("TX Packets:%-8lu", sc->tx_pkts);
    printf("    TX Deferred:%-8lu", sc->tx_deferred);
    printf("   TX Underrun:%-8lu\n", sc->tx_underrun);
    printf("TX Aborted:%-8lu\n", sc->tx_aborted);

}


/*  Driver ioctl handler */
static int
au1x00_emac_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
{
    au1x00_emac_softc_t *sc = ifp->if_softc;
    int error = 0;

    switch (command) {
    case SIOCGIFADDR:
    case SIOCSIFADDR:
        ether_ioctl (ifp, command, data);
        break;

    case SIOCSIFFLAGS:
        switch (ifp->if_flags & (IFF_UP | IFF_RUNNING))
        {
        case IFF_RUNNING:
            au1x00_emac_stop (sc);
            break;

        case IFF_UP:
            au1x00_emac_init (sc);
            break;

        case IFF_UP | IFF_RUNNING:
            au1x00_emac_stop (sc);
            au1x00_emac_init (sc);
            break;

        default:
            break;
        } /* switch (if_flags) */
        break;

    case SIO_RTEMS_SHOW_STATS:
        au1x00_emac_stats (sc);
        break;

        /*
         * FIXME: All sorts of multicast commands need to be added here!
         */
    default:
        error = EINVAL;
        break;
    } /* switch (command) */
    return error;
}

/* interrupt handler */
rtems_isr au1x00_emac_isr (rtems_vector_number v)
{
    volatile au1x00_emac_softc_t *sc;
    int tx_flag = 0;
    int rx_flag = 0;

    sc = &softc[0];
    if (v != AU1X00_IRQ_MAC0) {
      assert(v == AU1X00_IRQ_MAC0);
    }

    sc->interrupts++;

    /*
     * Since there's no easy way to find out the source of the
     * interrupt, we have to look at the tx and rx dma buffers
     */
    /* receive interrupt */
    while(sc->rx_dma[sc->rx_tail].addr & AU1X00_MAC_DMA_RXADDR_DN) {
        rx_flag = 1;
        sc->rx_interrupts++;
        sc->rx_dma[sc->rx_tail].addr &= ~AU1X00_MAC_DMA_RXADDR_DN;
        au_sync();

        sc->rx_tail++;
        if (sc->rx_tail >= NUM_RX_DMA_BUFS) {
            sc->rx_tail = 0;
        }
    }
    if (rx_flag != 0) {
        rtems_bsdnet_event_send(sc->rx_daemon_tid, START_RX_EVENT);
    }

    /* transmit interrupt */
    while (sc->tx_dma[sc->tx_tail].addr & AU1X00_MAC_DMA_TXADDR_DN) {
        uint32_t status;
        tx_flag = 1;
        sc->tx_interrupts++;

        status = sc->tx_dma[sc->tx_tail].stat;
        if (status & AU1X00_MAC_DMA_TXSTAT_DF) {
            sc->tx_deferred++;
        }
        if (status & AU1X00_MAC_DMA_TXSTAT_UR) {
            sc->tx_underrun++;
        }
        if (status & AU1X00_MAC_DMA_TXSTAT_FA) {
            sc->tx_aborted++;
        }

        sc->tx_dma[sc->tx_tail].addr = 0;
        au_sync();

        sc->tx_tail++;
        if (sc->tx_tail >= NUM_TX_DMA_BUFS) {
            sc->tx_tail = 0;
        }
    }
    if (tx_flag != 0) {
        rtems_bsdnet_event_send(sc->tx_daemon_tid, START_TX_EVENT);
    }
}