summaryrefslogblamecommitdiffstats
path: root/c/src/libchip/network/sonic.c
blob: 093cc0d0b39904693303541fd23d73082bed5c4b (plain) (tree)
1
2
3
4
5
6
7
8
  




                                                          
              
  


                                                                               
  





                                                                    





                                                           








                                                                   
   
 
                  
                               
                          
 
                  
                   

                  
                        










                             

  




                                                 


                                          

  






                                              






                                             


                                                                 


                                                                
   
                          






                                                           





                                                









                                                                      

                                                                










                                                                             
                    
    

                                                                 
     
                                          





                                              
                                          

    





                                                  




                                         





                                            
                             
     






                                                  





                                           
                                      
                                      
                                      
                                           



                         
                                           

                                                                



               
                                             


















                                                       
                                                    
 













































                                                                   







                                                                   
                             
                         


                  
                       


                                   
                            

                       
                                                       



                                  










































                                                        



                                                                               
                                                       








                                                    
                                           

                                 
                             




                                 
                                               

                                                         




                           
   
 
                                                     
 


                                        



                                       
                                        




                            
                                                      
 

















                                                                    








                                                                   

                                                                      
 
                                       
                      
           





                                           
                        
            
                               






                                        
                 
 
                   
 

                                                  
 


                                                     
                                             
                                     

                                                        

                                                                



                                                           

                                                                  
     
                                              

                                                        
                                                                               
   
 












                                                                   
 
                                                           


                          
                     



                                                   
                                               
 
                                           
                                                              

      
                                      





                                                                           

                                                   

      











                                                   



                                               
                                   

                                       
                             
 
                                                          
                                   








                                                                                    
                                
          

                                    

                                
                                 
                                
                            
                                
                                
                                
                       
                               
                       
                                 
                          

      
                                                    
       




                           
 
      

                                                                

                                   
 


                                           
                                    
                                           
                                           
      



   
              
   
                                                                      
 
                                         
                        



                                                 
                         

                         
                                             

 
    


                                                                           
     
 

                                          
    
                                 
     
                          


                                        

                                      
                                                  
      
        


                                              
         

        
                                                                            
         
                                                
                                       

                             

                                               


    




                                                                

                          


                                                              








                                 
                                         

                                                                              
      







                                                                                 







                      


                                                                   
                  







                                                                 
         


                               



                                                         

                          





                             
                          




                                            
 
                                  
                                              

                                         
                    
 







                                                 
 
 



















                                                     
 













                                             












                                                                   

                                 
                         

                                

        

                         




























                                                                      
                                                               




                                                             



                                                              



                                             
              
       



                                                     
                                                             




                                        
                    












                                                             


                                                        

                          
                                                     
            
                                

                           
                                                                                
            




                          

                                             


      

                                               
                                  




                         
                                                            



                          





                                 
   


                                           





                                                             

 

































                                                                      
  
                    
   
                                            
 



                                                     
                          
                                 

                                                  
 


                



                       
                                                             



                               



                                                     
                                  
                               

     


                                                                     
 




                                  
                              


              






                                                  

                                                  



                                                                                     
      
                                                                           
                     




                                                      
  



                                                             




                                                              
                                            



                                                                                         
                               

        







                                                        

                                                 


                                        
      



                             
 


                                   
                     
                           



                                
 

                                 

                                 
            
 

                                      
                                                               
      
                      
       
                                                            



                                          
                                                              
                                            


                                  
                          
                                   
                         
                                        
                       




                           
                                                               
                                           
                                                                 



                                      
                                                     
       
 
                              
                           

                                        
                    
 













                                                                   
                                                                   
 
                       

                        
                              
                                  
                                       
                               

                 
                             

    






                                                                        
                                                                       



                                                                           




                                                                
     
 
                                                           
                                      
                                          
      

                                        
      

                                                       

      



                                                              
                                  
                                          
  
              
                                        
 




                                                            


                                                  
          
   
        



                                                  


                                                             
                                                             

                                                                       

     

                                                             
                                          

                                                                
                                      

                                       
      
 

                                        
      

                                                                  
       


                                                   
               
          
   



                                                                   

                                             
                      
 






                                                                   
 
                                                                               
                                      
                                       






                                           

                                                                   






                                                                 



                                          
                         
 
                         



                       

                               

                               


                                                  
   
                
                                      
                                       

      



                            
                                                                                 



                                         

                                                             
 
                                                                        

    
                        
     
                                                                          
 


                                  
                                                     

    
                              
     
 



                                                      
 
    


                                                   
     
 
                          
                                                        
      
                                                  



                                  
 

                                                            
 
                                                           
 

                                                                             
 

                                                            
 

                                                                





                                                              
                                                           




                                

                                                            

             



                        
                                               

    
                                                     
     
 

                                
 



                                                                        
 





                                                                           


                                                                          
 
                                                            




                                         
 
                                                                                 
 
                                   
                                                                                 


                                                       
                                                 



                                                   
 




                                                                                 


                                                               
                                                 



                                                   
                               
   
      
 
                                                                          



                                   
      
                                                
  
                                                                   







                                              































                                                          
                                                                              
                                                                              




                            
                                                  



                                  
                                                 








                                       

                                                                       











                                                          


                                       

            




                                                       
 


                          
 







                                  
            
 


                              
    





                                                                    




               

                                                    
   
 
   
                           


                                       
 


                         

                 

    
                      
     














                                                                            


    


                                    
                               
 

    
                    
     

                                                                            
   









                                                        
                                   


                                      
                                   
                                                  
 





                                            
 
    
                                    
     
                     

                            







                                              

    
                         
     


                       
 
 
                                               
                  
 


































































                             
/*
 *       RTEMS NETWORK DRIVER FOR NATIONAL DP83932 `SONIC'
 *         SYSTEMS-ORIENTED NETWORK INTERFACE CONTROLLER
 *
 *                     REUSABLE CHIP DRIVER
 *
 * References:
 *
 *  1) DP83932C-20/25/33 MHz SONIC(TM) Systems-Oriented Network Interface
 *     Controller data sheet.  TL/F/10492, RRD-B30M105, National Semiconductor,
 *     1995.
 *
 *  2) Software Driver Programmer's Guide for the DP83932 SONIC(TM),
 *     Application Note 746, Wesley Lee and Mike Lui, TL/F/11140,
 *     RRD-B30M75, National Semiconductor, March, 1991.
 *
 *  COPYRIGHT (c) 1989-1997.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.OARcorp.com/rtems/license.html.
 *
 *  $Id$
 *
 *  This driver was originally written and tested on a DY-4 DMV177,
 *  which had a 100 Mhz PPC603e.
 *
 *  This driver also works with DP83934CVUL-20/25 MHz, tested on
 *  Tharsys ERC32 VME board. 
 *
 *  Rehaul to fix lost interrupts and buffers, and to use to use 
 *  interrupt-free transmission by Jiri, 22/03/1999.
 */

#include <rtems.h>
#include <rtems/rtems_bsdnet.h>
#include <libchip/sonic.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 <sys/sockio.h>

#include <net/if.h>

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

/*
 *  XXX fix this 
 */

void *set_vector(void *, unsigned32, unsigned32);

#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_MBUFS)
#include <rtems/dumpbuf.h>
#endif

/*
 *  Use the top line if you want more symbols.
 */

#define SONIC_STATIC 
/* #define SONIC_STATIC static */

/*
 * Number of devices supported by this driver
 */
#ifndef NSONIC
# define NSONIC 1
#endif

/*
 * 
 * As suggested by National Application Note 746, make the
 * receive resource area bigger than the receive descriptor area.
 *
 * NOTE:  Changing this may break this driver since it currently
 *        assumes a 1<->1 mapping.
 */
#define RRA_EXTRA_COUNT  0

/*
 * RTEMS event used by interrupt handler to signal daemons.
 */
#define INTERRUPT_EVENT  RTEMS_EVENT_1

/*
 * RTEMS event used to start transmit daemon.
 * This must not be the same as INTERRUPT_EVENT.
 */
#define START_TRANSMIT_EVENT    RTEMS_EVENT_2

/*
 * Largest Ethernet frame.
 */
#define MAXIMUM_FRAME_SIZE  1518

/*
 * Receive buffer size.
 * Allow for a pointer, plus a full ethernet frame (including Frame
 * Check Sequence) rounded up to a 4-byte boundary.
 */
#define RBUF_SIZE  ((sizeof (void *) + (MAXIMUM_FRAME_SIZE) + 3) & ~3)
/* #define RBUF_WC    ((((MAXIMUM_FRAME_SIZE) + 3) & ~3) / 2) */
#define RBUF_WC    (RBUF_SIZE / 2)

/*
 * Macros for manipulating 32-bit pointers as 16-bit fragments
 */
#define LSW(p)   ((rtems_unsigned16)((rtems_unsigned32)(p)))
#define MSW(p)   ((rtems_unsigned16)((rtems_unsigned32)(p) >> 16))
#define PTR(m,l) ((void*)(((rtems_unsigned16)(m)<<16)|(rtems_unsigned16)(l)))

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

  /*
   * Default location of device registers
   * ===CACHE===
   * This area must be non-cacheable, guarded.
   */
  void                             *sonic;

  /*
   *  Register access routines 
   */
  sonic_write_register_t           write_register;
  sonic_read_register_t            read_register;
  
  /*
   * Interrupt vector
   */
  rtems_vector_number             vector;

  /*
   * Data Configuration Register values
   */
  rtems_unsigned32                dcr_value;
  rtems_unsigned32                dc2_value;

  /*
   *  Indicates configuration
   */
  int                             acceptBroadcast;

  /*
   * Task waiting for interrupts
   */
  rtems_id                        rxDaemonTid;
  rtems_id                        txDaemonTid;

  /*
   * Receive resource area
   */
  int                             rdaCount;
  ReceiveResourcePointer_t        rsa;
  ReceiveResourcePointer_t        rea;
  CamDescriptorPointer_t          cdp;
  ReceiveDescriptorPointer_t      rda;
  ReceiveDescriptorPointer_t      rdp_last;

  /*
   * Transmit descriptors
   */
  int                             tdaCount;
  TransmitDescriptorPointer_t     tdaHead;  /* Last filled */
  TransmitDescriptorPointer_t     tdaTail;  /* Next to retire */

  /*
   * Statistics
   */
  unsigned long                   Interrupts;
  unsigned long                   rxInterrupts;
  unsigned long                   rxMissed;
  unsigned long                   rxGiant;
  unsigned long                   rxNonOctet;
  unsigned long                   rxBadCRC;
  unsigned long                   rxCollision;

  unsigned long                   txInterrupts;
  unsigned long                   txSingleCollision;
  unsigned long                   txMultipleCollision;
  unsigned long                   txCollision;
  unsigned long                   txDeferred;
  unsigned long                   txUnderrun;
  unsigned long                   txLateCollision;
  unsigned long                   txExcessiveCollision;
  unsigned long                   txExcessiveDeferral;
  unsigned long                   txLostCarrier;
  unsigned long                   txRawWait;
};
SONIC_STATIC struct sonic_softc sonic_softc[NSONIC];


/*
 ******************************************************************
 *                                                                *
 *                         Debug Routines                         *
 *                                                                *
 ******************************************************************
 */

#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
void sonic_print_tx_descriptor(
  TransmitDescriptorPointer_t tdp
)
{
  printf( "TXD ==> %p", tdp );
  printf( "  pkt_config = 0x%04x", tdp->pkt_config & 0xffff);
  printf( "  pkt_size = 0x%04x\n", tdp->pkt_size & 0xffff );
  printf( "  frag_count = %d", tdp->frag_count & 0xffff );
  /* could print all the fragments */
  printf( "  next = %p", tdp->next );
  printf( "  linkp = %p\n", tdp->linkp );
  printf( "  mbufp = %p", tdp->mbufp );
  if ( tdp->mbufp )
    printf( "  mbufp->data = %p", mtod ( tdp->mbufp, void *) );
  puts("");
}

void sonic_print_rx_descriptor(
  ReceiveDescriptorPointer_t rdp
)
{
  printf( "RXD ==> %p\n", rdp );
  printf( "  status = 0x%04x", rdp->status & 0xffff );
  printf( "  byte_count = 0x%04x\n", rdp->byte_count & 0xffff );
  printf( "  pkt = 0x%04x%04x", rdp->pkt_msw, rdp->pkt_lsw );
  printf( "  seq_no = %d", rdp->seq_no );
  printf( "  link = %d\n", rdp->link );
  printf( "  in_use = %d", rdp->in_use );
  printf( "  next = %p", rdp->next );
  printf( "  mbufp = %p", rdp->mbufp );
  if ( rdp->mbufp )
    printf( "  mbufp->data = %p", mtod ( rdp->mbufp, void *) );
  puts("");
}
#endif

/*
 ******************************************************************
 *                                                                *
 *                        Support Routines                        *
 *                                                                *
 ******************************************************************
 */

void sonic_enable_interrupts(
  struct sonic_softc *sc,
  unsigned32  mask
)
{
  void *rp = sc->sonic;
  rtems_interrupt_level level;

  rtems_interrupt_disable( level );
      (*sc->write_register)(
         rp,
         SONIC_REG_IMR,
         (*sc->read_register)(rp, SONIC_REG_IMR) | mask
      );
  rtems_interrupt_enable( level );
}

void sonic_disable_interrupts(
  struct sonic_softc *sc,
  unsigned32  mask
)
{
  void *rp = sc->sonic;
  rtems_interrupt_level level;

  rtems_interrupt_disable( level );
  (*sc->write_register)(
         rp,
         SONIC_REG_IMR,
         (*sc->read_register)(rp, SONIC_REG_IMR) & ~mask
      );
  rtems_interrupt_enable( level );
}

void sonic_clear_interrupts(
  struct sonic_softc *sc,
  unsigned32  mask
)
{
  void *rp = sc->sonic;
  rtems_interrupt_level level;

  rtems_interrupt_disable( level );
  (*sc->write_register)( rp, SONIC_REG_ISR, mask);
  rtems_interrupt_enable( level );
}

void sonic_command(
  struct sonic_softc *sc,
  unsigned32  mask
)
{
  void *rp = sc->sonic;
  rtems_interrupt_level level;

  rtems_interrupt_disable( level );
  (*sc->write_register)( rp, SONIC_REG_CR, mask);
  rtems_interrupt_enable( level );
}

/*
 * Allocate non-cacheable memory on a single 64k page.
 * Very simple minded -- just keeps trying till the memory is on a single page.
 */
SONIC_STATIC void * sonic_allocate(unsigned int nbytes)
{
  void *p;
  unsigned long a1, a2;

  for (;;) {
    /*
     * ===CACHE===
     * Change malloc to malloc_noncacheable_guarded.
     */
    p = malloc( nbytes, M_MBUF, M_NOWAIT );
    if (p == NULL)
      rtems_panic ("No memory!");
    memset (p, '\0', nbytes);
    a1 = (unsigned long)p;
    a2 = a1 + nbytes - 1;
    if ((a1 >> 16) == (a2 >> 16))
      break;
  }
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_ALLOCATE)
  printf( "sonic_allocate %d bytes at %p\n", nbytes, p );
#endif
  return p;
}

/*
 * Shut down the interface.
 */

SONIC_STATIC void sonic_stop (struct sonic_softc *sc)
{
  struct ifnet *ifp = &sc->arpcom.ac_if;

  ifp->if_flags &= ~IFF_RUNNING;

  /*
   * Stop the transmitter and receiver.
   */
  sonic_command(sc, CR_HTX | CR_RXDIS );
}

/*
 * Show interface statistics
 */
SONIC_STATIC void sonic_stats (struct sonic_softc *sc)
{
  printf (" Total Interrupts:%-8lu", sc->Interrupts);
  printf ("    Rx Interrupts:%-8lu", sc->rxInterrupts);
  printf ("            Giant:%-8lu", sc->rxGiant);
  printf ("        Non-octet:%-8lu\n", sc->rxNonOctet);
  printf ("          Bad CRC:%-8lu", sc->rxBadCRC);
  printf ("        Collision:%-8lu", sc->rxCollision);
  printf ("           Missed:%-8lu\n", sc->rxMissed);

  printf (    "    Tx Interrupts:%-8lu", sc->txInterrupts);
  printf (  "           Deferred:%-8lu", sc->txDeferred);
  printf ("        Lost Carrier:%-8lu\n", sc->txLostCarrier);
  printf (   "Single Collisions:%-8lu", sc->txSingleCollision);
  printf ( "Multiple Collisions:%-8lu", sc->txMultipleCollision);
  printf ("Excessive Collisions:%-8lu\n", sc->txExcessiveCollision);
  printf (   " Total Collisions:%-8lu", sc->txCollision);
  printf ( "     Late Collision:%-8lu", sc->txLateCollision);
  printf ("            Underrun:%-8lu\n", sc->txUnderrun);
  printf (   "  Raw output wait:%-8lu\n", sc->txRawWait);
}

/*
 ******************************************************************
 *                                                                *
 *                        Interrupt Handler                       *
 *                                                                *
 ******************************************************************
 */

SONIC_STATIC rtems_isr sonic_interrupt_handler (rtems_vector_number v)
{
  struct sonic_softc *sc = sonic_softc;
  unsigned32 isr, imr;
  void *rp;

#if (NSONIC > 1)
  /*
   * Find the device which requires service
   */
  for (;;) {
    if (sc->vector == v)
      break;
    if (++sc == &sonic[NSONIC])
      return;  /* Spurious interrupt? */
  }
#endif /* NSONIC > 1 */

  /*
   * Get pointer to SONIC registers
   */
  rp = sc->sonic;

  sc->Interrupts++;

  isr = (*sc->read_register)( rp, SONIC_REG_ISR );
  imr = (*sc->read_register)( rp, SONIC_REG_IMR );

  /*
   * Packet received or receive buffer area exceeded?
   */
  if (imr & isr & (IMR_PRXEN | IMR_RBAEEN)) {
    imr &= ~(IMR_PRXEN | IMR_RBAEEN);
    sc->rxInterrupts++;
    rtems_event_send (sc->rxDaemonTid, INTERRUPT_EVENT);
    (*sc->write_register)( rp, SONIC_REG_IMR, imr );
    (*sc->write_register)( rp, SONIC_REG_ISR, isr & ISR_PKTRX );
  }

  /*
   * Packet started, transmitter done or transmitter error?
   * TX interrupts only occur after an error or when all TDA's are
   * exhausted and we are waiting for buffer to come free.
   */
  if (imr & isr & (IMR_PINTEN | IMR_TXEREN)) {
    sc->txInterrupts++;
    rtems_event_send (sc->txDaemonTid, INTERRUPT_EVENT);
    (*sc->write_register)( rp, SONIC_REG_ISR, ISR_PINT | ISR_TXDN | ISR_TXER );
  }

}

/*
 ******************************************************************
 *                                                                *
 *                      Transmitter Routines                      *
 *                                                                *
 ******************************************************************
 */

/*
 * Soak up transmit descriptors that have been sent.
 */

SONIC_STATIC void sonic_retire_tda (struct sonic_softc *sc)
{
  rtems_unsigned16 status;
  unsigned int collisions;
  struct mbuf *m, *n;

  /*
   * Repeat for all completed transmit descriptors.
   */
  while ((status = sc->tdaTail->status) != 0) {

#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
    printf( "retire TDA %p (0x%04x)\n", sc->tdaTail, status );
#endif

#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
    /*
     *  If there is an error that was not a collision, 
     *  then someone may want to see it.
     */

    if ( (status & ~(TDA_STATUS_COLLISION_MASK|TDA_STATUS_DEF)) != 0x0001 )
      printf( "ERROR: retire TDA %p (0x%08x)\n",
                sc->tdaTail, sc->tdaTail->status );
#endif

    /*
     * Check for errors which stop the transmitter.
     */
    if (status & (TDA_STATUS_EXD |
        TDA_STATUS_EXC |
        TDA_STATUS_FU |
        TDA_STATUS_BCM)) {
      /*
       * Restart the transmitter if there are
       * packets waiting to go.
       */
      rtems_unsigned16 link;
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
      printf("restarting sonic after error\n");
#endif

      link = *(sc->tdaTail->linkp);

      if ((link & TDA_LINK_EOL) == 0) {
        void *rp = sc->sonic;

        (*sc->write_register)( rp, SONIC_REG_CTDA, link );
        sonic_command(sc, CR_TXP );
      }
    }

    /*
     * Update network statistics
     */
    collisions = (status & TDA_STATUS_COLLISION_MASK) >> TDA_STATUS_COLLISION_SHIFT;
    if (collisions) {
      if (collisions == 1)
        sc->txSingleCollision++;
      else
        sc->txMultipleCollision++;
      sc->txCollision += collisions;
    }
    if (status & TDA_STATUS_EXC)
      sc->txExcessiveCollision++;
    if (status & TDA_STATUS_OWC)
      sc->txLateCollision++;
    if (status & TDA_STATUS_EXD)
      sc->txExcessiveDeferral++;
    if (status & TDA_STATUS_DEF)
      sc->txDeferred++;
    if (status & TDA_STATUS_FU)
      sc->txUnderrun++;
    if (status & TDA_STATUS_CRSL)
      sc->txLostCarrier++;

    /*
     *  Free the packet and reset a couple of fields
     */
    m = sc->tdaTail->mbufp;
    while ( m ) {
      MFREE(m, n);
      m = n;
    }

    /*
    sc->tdaTail->frag[0].frag_link = LSW(sc->tdaTail->link_pad);
    sc->tdaTail->frag_count        = 0;
    */
    sc->tdaTail->status        = 0;

    /*
     * Move to the next transmit descriptor
     */
    sc->tdaTail = sc->tdaTail->next;
#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
    printf( "next TDA %p\n", sc->tdaTail );
#endif
  }
}

/*
 * Send packet
 */
SONIC_STATIC void sonic_sendpacket (struct ifnet *ifp, struct mbuf *m)
{
  struct sonic_softc *sc = ifp->if_softc;
  struct mbuf *l = NULL;
  TransmitDescriptorPointer_t tdp;
  volatile struct TransmitDescriptorFragLink *fp;
  unsigned int packetSize;
  int i;
  rtems_event_set events;
  static char padBuf[64];

  /* printf( "sonic_sendpacket %p\n", m ); */


  /*
   * Wait for transmit descriptor to become available. Only retire TDA's
   * if there are no more free buffers to minimize TX latency. Retire TDA'a
   * on the way out.
   */

  while (sc->tdaHead->next->status != 0) {
  
  /*
   * Free up transmit descriptors
   */
    sonic_retire_tda (sc);

    if (sc->tdaHead->next->status == 0) 
      break;

#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
    printf("blocking until TDAs are available\n");
#endif
      /*
       * Enable PINT interrupts.
    sonic_clear_interrupts( sc, ISR_PINT );
    sonic_enable_interrupts( sc, IMR_PINTEN );
       */

      /*
       * Wait for PINT TX interrupt. Every fourth TX buffer will raise PINT.
       */
    rtems_bsdnet_event_receive (INTERRUPT_EVENT,
            RTEMS_WAIT|RTEMS_EVENT_ANY,
            RTEMS_NO_TIMEOUT,
            &events);
    sonic_disable_interrupts( sc, IMR_PINTEN );
    sonic_retire_tda (sc);
  }

  /*
   * Fill in the transmit descriptor fragment descriptors.
   * ===CACHE===
   * If data cache is operating in write-back mode, flush cached
   * data to memory.
   */
  tdp = sc->tdaHead->next;
  tdp->mbufp = m;
  packetSize = 0;
  fp = tdp->frag;
  for (i = 0 ; i < MAXIMUM_FRAGS_PER_DESCRIPTOR ; i++, fp++) {
    /*
     * Throw away empty mbufs
     */
    if (m->m_len) {
      void *p = mtod (m, void *);
      fp->frag_lsw = LSW(p);
      fp->frag_msw = MSW(p);
      fp->frag_size = m->m_len;
      packetSize += m->m_len;
#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
      printf( "fp %p 0x%04x%04x %d=%d .. %d\n",
        fp, fp->frag_msw, fp->frag_lsw, fp->frag_size, m->m_len, packetSize );
#endif
#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_TX_MBUFS)
      Dump_Buffer(
        p,
        (fp->frag_size > MAXIMUM_FRAME_SIZE) ? MAXIMUM_FRAME_SIZE : fp->frag_size
      );
#endif
      l = m;
      m = m->m_next;
    }
    else {
      struct mbuf *n;
      MFREE (m, n);
      m = n;
      if (l != NULL)
        l->m_next = m;
    }
    /*
     * Break out of the loop if this mbuf is the last in the frame.
     */
    if (m == NULL)
      break;
  }

  /*
   * Pad short packets.
   */
  if  ((packetSize < 64) && (i < MAXIMUM_FRAGS_PER_DESCRIPTOR)) {
    int padSize = 64 - packetSize;
    fp++;
    fp->frag_lsw = LSW(padBuf);
    fp->frag_msw = MSW(padBuf);
    fp->frag_size = padSize;
#if (SONIC_DEBUG & SONIC_DEBUG_FRAGMENTS)
    printf( "PAD fp %p 0x%04x%04x %d\n",
         fp, fp->frag_msw, fp->frag_lsw, fp->frag_size );
#endif
    packetSize += padSize;
    i++;
  }

  /*
   * Fill Transmit Descriptor
   */
  tdp->pkt_size = packetSize;
  tdp->frag_count = i + 1;
  tdp->status = 0;

  /*
   * Chain onto list and start transmission.
   */

  tdp->linkp = &(fp+1)->frag_link;
  *tdp->linkp = LSW(tdp->next) | TDA_LINK_EOL;
  if ( sc->tdaHead->frag_count )
    *sc->tdaHead->linkp &= ~TDA_LINK_EOL;
  sc->tdaHead = tdp;

  /* Start transmission */

  sonic_command(sc, CR_TXP );

  /*
   * Free up transmit descriptors on the way out.
   */
  sonic_retire_tda (sc);
}

/*
 * Driver transmit daemon
 */
SONIC_STATIC void sonic_txDaemon (void *arg)
{
  struct sonic_softc *sc = (struct sonic_softc *)arg;
  struct ifnet *ifp = &sc->arpcom.ac_if;
  struct mbuf *m;
  rtems_event_set events;

  for (;;) {
    /*
     * Wait for packet
     */
    rtems_bsdnet_event_receive (
       START_TRANSMIT_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;
      sonic_sendpacket (ifp, m);
    }
    ifp->if_flags &= ~IFF_OACTIVE;
  }
}

/*
 ******************************************************************
 *                                                                *
 *                        Receiver Routines                       *
 *                                                                *
 ******************************************************************
 */

/*
 * Wait for SONIC to hand over a Receive Descriptor.
 */

SONIC_STATIC void sonic_rda_wait(
  struct sonic_softc *sc,
  ReceiveDescriptorPointer_t rdp
)
{
  int i;
  void *rp = sc->sonic;
  rtems_event_set events;

  /*
   * Wait for Receive Descriptor.
   * The order of the tests is very important.
   *    The RDA is checked after RBAE is detected.  This ensures that
   *    the driver processes all RDA entries before reusing the RRA
   *    entry holding the giant packet.
   *    The event wait is done after the RDA and RBAE checks.  This
   *    catches the possibility that a Receive Descriptor became ready
   *    between the call to this function and the clearing of the
   *    interrupt status register bit.
   */
  for (;;) {
    /*
     * Has a giant packet arrived?
     * The National DP83932C data sheet is very vague on what
     * happens under this condition.  The description of the
     * Interrupt Status Register (Section 4.3.6) states,
     * ``Reception is aborted and the SONIC fetches the next
     * available resource descriptors in the RRA.  The buffer
     * space is not re-used and an RDA is not setup for the
     * truncated packet.''
     * I take ``Reception is aborted''  to mean that the RXEN
     * bit in the Command Register is cleared and must be set
     * by the driver to begin reception again.
     * Unfortunately, an alternative interpretation could be
     * that only reception of the current packet is aborted.
     * This would be more difficult to recover from....
     */
    if ((*sc->read_register)( rp, SONIC_REG_ISR ) & ISR_RBAE) {

#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
      printf( "ERROR: looks like a giant packet -- RBAE\n" );
#endif

      /*
       * One more check to soak up any Receive Descriptors
       * that may already have been handed back to the driver.
       */
      if (rdp->in_use == RDA_IN_USE) {
#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
      printf( "ERROR: nope just an RBAE\n" );
#endif
        break;
      }

      /*
       * Check my interpretation of the SONIC manual.
       */
      if ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_RXEN)
        rtems_panic ("SONIC RBAE/RXEN");

      /*
       * Update statistics
       */
      sc->rxGiant++;

      /*
       * Reuse receive buffer.
       * Again, the manual is subject to interpretation.  The
       * RRP register is described as, `the lower address of
       * the next descriptor the SONIC will read.''
       * Since, acording to the ISR/RBAE notes, the SONIC has
       * ``fetched the next available resource descriptor in
       * the RRA'', I interpret this to mean that that the
       * driver has to move the RRP back *two* entries to
       * reuse the receive buffer holding the giant packet.
       */
      for (i = 0 ; i < 2 ; i++) {
        if ((*sc->read_register)( rp, SONIC_REG_RRP ) ==
            (*sc->read_register)( rp, SONIC_REG_RSA ))
          (*sc->write_register)(
            rp,
            SONIC_REG_RRP,
            (*sc->read_register)( rp, SONIC_REG_REA )
          );
          (*sc->write_register)(
             rp,
             SONIC_REG_RRP,
             (*sc->read_register)(rp, SONIC_REG_RRP) - sizeof(ReceiveResource_t)
          );
      }

      /*
       * Restart reception
       */
      sonic_clear_interrupts( sc, ISR_RBAE );
      sonic_command( sc, CR_RXEN );
    }

    /*
     * Has Receive Descriptor become available?
     */
    if (rdp->in_use == RDA_IN_USE)
      break;

    /*
     * Enable interrupts.
     */
    sonic_enable_interrupts( sc, (IMR_PRXEN | IMR_RBAEEN) );

    /*
     * Wait for interrupt.
     */
    rtems_bsdnet_event_receive(
      INTERRUPT_EVENT,
      RTEMS_WAIT|RTEMS_EVENT_ANY,
      RTEMS_NO_TIMEOUT,
      &events
    );
  }
#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
  printf( "RDA %p\n", rdp );
#endif

#if (SONIC_DEBUG & SONIC_DEBUG_ERRORS)
    if (rdp->status & 0x000E)
      printf( "ERROR: RDA %p (0x%04x)\n", rdp, rdp->status );
#endif

}

#ifdef CPU_U32_FIX

/*
 * Routine to align the received packet so that the ip header
 * is on a 32-bit boundary. Necessary for cpu's that do not
 * allow unaligned loads and stores and when the 32-bit DMA
 * mode is used. 
 * 
 * Transfers are done on word basis to avoid possibly slow byte 
 * and half-word writes.
 */
 
void ipalign(struct mbuf *m)
{
  unsigned int *first, *last, data;
  unsigned int tmp = 0;

  if ((((int) m->m_data) & 2) && (m->m_len)) {
    last = (unsigned int *) ((((int) m->m_data) + m->m_len + 8) & ~3);
    first = (unsigned int *) (((int) m->m_data) & ~3);
    tmp = *first << 16;
    first++;
    do {
      data = *first;
      *first = tmp | (data >> 16);
      tmp = data << 16;
      first++;
    } while (first <= last);

    m->m_data = (caddr_t)(((int) m->m_data) + 2);
  }
}
#endif

/*
 * SONIC reader task
 */
SONIC_STATIC void sonic_rxDaemon (void *arg)
{
  struct sonic_softc *sc = (struct sonic_softc *)arg;
  struct ifnet *ifp = &sc->arpcom.ac_if;
  void *rp = sc->sonic;
  struct mbuf *m;
  rtems_unsigned16 status;
  ReceiveDescriptorPointer_t rdp;
  ReceiveResourcePointer_t rwp, rea;
  rtems_unsigned16 newMissedTally, oldMissedTally;

  rwp = sc->rsa;
  rea = sc->rea;
  rdp = sc->rda;

  /*
   * Start the receiver
   */
  oldMissedTally = (*sc->read_register)( rp, SONIC_REG_MPT );

  /*
   * Input packet handling loop
   */
  for (;;) {
    /*
     * Wait till SONIC supplies a Receive Descriptor.
     */
    if (rdp->in_use == RDA_FREE) {
      sonic_rda_wait (sc, rdp);
    }

#if (SONIC_DEBUG & SONIC_DEBUG_DESCRIPTORS)
    printf( "Incoming packet %p status=0x%04x\n", rdp, rdp->status );
#endif

    /*
     * Check that packet is valid
     */
    status = rdp->status;
    if (status & RDA_STATUS_PRX) {
      struct ether_header *eh;
      void *p;

      /*
       * Pass the packet up the chain.
       * The mbuf count is reduced to remove
       * the frame check sequence at the end
       * of the packet.
       * ===CACHE===
       * Invalidate cache entries for this memory.
       */
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
      sonic_print_rx_descriptor( rdp );
      if ((LSW(rdp->mbufp->m_data) != rdp->pkt_lsw)
       || (MSW(rdp->mbufp->m_data) != rdp->pkt_msw))
        printf ("SONIC RDA/RRA %p, %08x\n",rdp->mbufp->m_data,(rdp->pkt_msw << 16) | 
	(rdp->pkt_lsw & 0x0ffff));
#endif
      rdp->byte_count &= 0x0ffff;    /* ERC32 pollutes msb of byte_count */
      m = rdp->mbufp;
      m->m_len = m->m_pkthdr.len = rdp->byte_count -
                          sizeof(rtems_unsigned32) -
                          sizeof(struct ether_header);
      eh = mtod (m, struct ether_header *);
      m->m_data += sizeof(struct ether_header);
  
#ifdef CPU_U32_FIX
      ipalign(m);	/* Align packet on 32-bit boundary */
#endif

#if (SONIC_DEBUG & SONIC_DEBUG_DUMP_RX_MBUFS)
      Dump_Buffer( (void *) eh, sizeof(struct ether_header) );
      Dump_Buffer( (void *) m, 96 /* m->m_len*/ );
#endif

      /* printf( "ether_input %p\n", m ); */
      /*
      printf( "pkt %p, seq %04x, mbuf %p, m_data %p\n", rdp, rdp->seq_no, m, m->m_data );
      printf( "%u, %u\n", ((int*)m->m_data)[6], ((int*)m->m_data)[7]);
      */
      ether_input (ifp, eh, m);
      /*
      */

      /*
       * Sanity check that Receive Resource Area is
       * still in sync with Receive Descriptor Area
       * The buffer reported in the Receive Descriptor
       * should be the same as the buffer in the Receive
       * Resource we are about to reuse.
       */
/* XXX figure out whether this is valid or not */
#if 0
      if ((LSW(p) != rwp->buff_ptr_lsw)
       || (MSW(p) != rwp->buff_ptr_msw))
        rtems_panic ("SONIC RDA/RRA");
#endif

      /*
       * Allocate a new mbuf.
       */

      MGETHDR (m, M_WAIT, MT_DATA);
      MCLGET (m, M_WAIT);
      m->m_pkthdr.rcvif = ifp;
      rdp->mbufp = m;
      p = mtod (m, void *);

      /*
       * Reuse Receive Resource.
       */

      rwp->buff_ptr_lsw = LSW(p);
      rwp->buff_ptr_msw = MSW(p);
      rwp->buff_wc_lsw = RBUF_WC;
      rwp->buff_wc_msw = 0;
      rwp++;

      if (rwp == rea) {
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
        printf( "Wrapping RWP from %p to %p\n", rwp, sc->rsa );
#endif
        rwp = sc->rsa;
      }
      (*sc->write_register)( rp, SONIC_REG_RWP , LSW(rwp) );

      /*
       * Tell the SONIC to reread the RRA.
       */
      if ((*sc->read_register)( rp, SONIC_REG_ISR ) & ISR_RBE)
      sonic_clear_interrupts( sc, ISR_RBE );
    }
    else {
      if (status & RDA_STATUS_COL)
        sc->rxCollision++;
      if (status & RDA_STATUS_FAER)
        sc->rxNonOctet++;
      else if (status & RDA_STATUS_CRCR)
        sc->rxBadCRC++;
    }

    /*
     * Count missed packets
     */
    newMissedTally = (*sc->read_register)( rp, SONIC_REG_MPT );
    if (newMissedTally != oldMissedTally) {
      sc->rxMissed += (newMissedTally - oldMissedTally) & 0xFFFF;
      newMissedTally = oldMissedTally;
    }

    /*
     * Move to next receive descriptor and update EOL
     */

    rdp->link |= RDA_LINK_EOL;
    rdp->in_use = RDA_FREE;
    sc->rdp_last->link &= ~RDA_LINK_EOL;
    sc->rdp_last = rdp;
    rdp = rdp->next;

  }
}

/*
 ******************************************************************
 *                                                                *
 *                     Initialization Routines                    *
 *                                                                *
 ******************************************************************
 */

/*
 * Initialize the SONIC hardware
 */
SONIC_STATIC void sonic_initialize_hardware(struct sonic_softc *sc)
{
  void *rp = sc->sonic;
  int i;
  unsigned char *hwaddr;
  rtems_isr_entry old_handler;
  TransmitDescriptorPointer_t tdp;
  ReceiveDescriptorPointer_t ordp, rdp;
  ReceiveResourcePointer_t rwp;
  struct mbuf *m;
  void *p;
  CamDescriptorPointer_t cdp;

  /*
   *  The Revision B SONIC has a horrible bug known as the "Zero
   *  Length Packet bug".  The initial board used to develop this
   *  driver had a newer revision of the SONIC so there was no reason
   *  to check for this.  If you have the Revision B SONIC chip, then
   *  you need to add some code to the RX path to handle this weirdness.
   */

  if ( (*sc->read_register)( rp, SONIC_REG_SR ) <= SONIC_REVISION_B ) {
    rtems_fatal_error_occurred( 0x0BADF00D );  /* don't eat this part :) */
  }
  
  /*
   *  Set up circular linked list in Transmit Descriptor Area.
   *  Use the PINT bit in the transmit configuration field to
   *  request an interrupt on every other transmitted packet.
   *
   *  NOTE: sonic_allocate() zeroes all of the memory allocated.
   */

  sc->tdaTail = sonic_allocate(sc->tdaCount * sizeof *tdp);
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
  printf( "tdaTail = %p\n", sc->tdaTail );
#endif
  tdp = sc->tdaTail;
  for (i = 0 ; i < sc->tdaCount ; i++) {
    /*
     *  Start off with the table of outstanding mbuf's 
     */

    /*
     *  status, pkt_config, pkt_size, and all fragment fields 
     *  are set to zero by sonic_allocate.
     */

/* XXX not used by the BSD drivers
    tdp->frag[0].frag_link = LSW(tdp + 1);
*/
    if (i & 3)
      tdp->pkt_config = TDA_CONFIG_PINT;

    tdp->status 	= 0;
    tdp->frag_count     = 0;
    tdp->link_pad       = LSW(tdp + 1) | TDA_LINK_EOL;
    tdp->linkp          = &((tdp + 1)->frag[0].frag_link);
    tdp->next           = (TransmitDescriptor_t *)(tdp + 1);
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
    sonic_print_tx_descriptor( tdp );
#endif
    tdp++;
  }
  tdp--;
  sc->tdaHead = tdp;
  tdp->link_pad = LSW(sc->tdaTail) | TDA_LINK_EOL;
  tdp->next = (TransmitDescriptor_t *)sc->tdaTail;
  tdp->linkp = &sc->tdaTail->frag[0].frag_link;

  /*
   *  Set up circular linked list in Receive Descriptor Area.
   *  Leaves sc->rda pointing at the `beginning' of the list.
   *
   *  NOTE: The RDA and CDP must have the same MSW for their addresses.
   */

  sc->rda = sonic_allocate(
              (sc->rdaCount * sizeof(ReceiveDescriptor_t)) + 
                sizeof(CamDescriptor_t) );
  sc->cdp = (CamDescriptorPointer_t) ((unsigned char *)sc->rda +
        (sc->rdaCount * sizeof(ReceiveDescriptor_t)));
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
  printf( "rda area = %p\n", sc->rda );
  printf( "cdp area = %p\n", sc->cdp );
#endif

  ordp = rdp = sc->rda;
  for (i = 0 ; i < sc->rdaCount ; i++) {
    /*
     *  status, byte_count, pkt_ptr0, pkt_ptr1, and seq_no are set
     *  to zero by sonic_allocate.
     */
    rdp->link   = LSW(rdp + 1);
    rdp->in_use = RDA_FREE;
    rdp->next   = (ReceiveDescriptor_t *)(rdp + 1);
    ordp = rdp;
    rdp++;
  }
  /*
   *  Link the last desriptor to the 1st one and mark it as the end
   *  of the list.
   */
  ordp->next   = sc->rda;
  ordp->link   = LSW(sc->rda) | RDA_LINK_EOL;
  sc->rdp_last = ordp;
 
  /*
   * Allocate the receive resource area.
   * In accordance with National Application Note 746, make the
   * receive resource area bigger than the receive descriptor area.
   * This has the useful side effect of making the receive resource
   * area big enough to hold the CAM descriptor area.
   */

  sc->rsa = sonic_allocate((sc->rdaCount + RRA_EXTRA_COUNT) * sizeof *sc->rsa);
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
  printf( "rsa area = %p\n", sc->rsa );
#endif

  /*
   *  Set up list in Receive Resource Area.
   *  Allocate space for incoming packets.
   */

  rwp = sc->rsa;
  for (i = 0 ; i < (sc->rdaCount + RRA_EXTRA_COUNT) ; i++, rwp++) {

    /*
     * Allocate memory for buffer.
     * Place a pointer to the mbuf at the beginning of the buffer
     * so we can find the mbuf when the SONIC returns the buffer
     * to the driver.
     */
    
    MGETHDR (m, M_WAIT, MT_DATA);
    MCLGET (m, M_WAIT);
    m->m_pkthdr.rcvif = &sc->arpcom.ac_if;
    sc->rda[i].mbufp = m;

    p = mtod (m, void *);

    /*
     * Set up RRA entry
     */
    rwp->buff_ptr_lsw = LSW(p);
    rwp->buff_ptr_msw = MSW(p);
    rwp->buff_wc_lsw = RBUF_WC;
    rwp->buff_wc_msw = 0;
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY_DESCRIPTORS)
    sonic_print_rx_descriptor( &sc->rda[i] );
#endif
  }
  sc->rea = rwp;
#if (SONIC_DEBUG & SONIC_DEBUG_MEMORY)
  printf( "rea area = %p\n", sc->rea );
#endif


  /*
   * Issue a software reset.
   */
  (*sc->write_register)( rp, SONIC_REG_CR, CR_RST | CR_STP | CR_RXDIS | CR_HTX );

  /*
   * Set up data configuration registers.
   */
  (*sc->write_register)( rp, SONIC_REG_DCR, sc->dcr_value );
  (*sc->write_register)( rp, SONIC_REG_DCR2, sc->dc2_value );

  (*sc->write_register)( rp, SONIC_REG_CR, CR_STP | CR_RXDIS | CR_HTX );

  /*
   * Mask all interrupts
   */
  (*sc->write_register)( rp, SONIC_REG_IMR, 0x0 ); /* XXX was backwards */

  /*
   * Clear outstanding interrupts.
   */
  (*sc->write_register)( rp, SONIC_REG_ISR, 0x7FFF );

  /*
   *  Clear the tally counters
   */

  (*sc->write_register)( rp, SONIC_REG_CRCT, 0xFFFF );
  (*sc->write_register)( rp, SONIC_REG_FAET, 0xFFFF );
  (*sc->write_register)( rp, SONIC_REG_MPT, 0xFFFF );
  (*sc->write_register)( rp, SONIC_REG_RSC, 0 );

  /*
   *  Set the Receiver mode
   *
   *  Enable/disable reception of broadcast packets
   */

  if (sc->acceptBroadcast)
    (*sc->write_register)( rp, SONIC_REG_RCR, RCR_BRD );
  else
    (*sc->write_register)( rp, SONIC_REG_RCR, 0 );

  /*
   * Set up Resource Area pointers
   */

  (*sc->write_register)( rp, SONIC_REG_URRA, MSW(sc->rsa) );
  (*sc->write_register)( rp, SONIC_REG_RSA, LSW(sc->rsa) );

  (*sc->write_register)( rp, SONIC_REG_REA, LSW(sc->rea) );

  (*sc->write_register)( rp, SONIC_REG_RRP, LSW(sc->rsa) );
  (*sc->write_register)( rp, SONIC_REG_RWP, LSW(sc->rsa) ); /* XXX was rea */

  (*sc->write_register)( rp, SONIC_REG_URDA, MSW(sc->rda) );
  (*sc->write_register)( rp, SONIC_REG_CRDA, LSW(sc->rda) );

  (*sc->write_register)( rp, SONIC_REG_UTDA, MSW(sc->tdaTail) );
  (*sc->write_register)( rp, SONIC_REG_CTDA, LSW(sc->tdaTail) );

  /*
   * Set End Of Buffer Count register to the value recommended
   * in Note 1 of Section 3.4.4.4 of the SONIC data sheet.
   */

  (*sc->write_register)( rp, SONIC_REG_EOBC, RBUF_WC - 2 );

  /*
   *  Issue the load RRA command
   */

  (*sc->write_register)( rp, SONIC_REG_CR, CR_RRRA );
  while ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_RRRA)
    continue;

  /*
   * Remove device reset
   */

  (*sc->write_register)( rp, SONIC_REG_CR, 0 );

  /*
   *  Set up the SONIC CAM with our hardware address.
   */

  hwaddr = sc->arpcom.ac_enaddr;
  cdp = sc->cdp;

#if (SONIC_DEBUG & SONIC_DEBUG_CAM)
  printf( "hwaddr: %2x:%2x:%2x:%2x:%2x:%2x\n",
     hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5] );
#endif

  cdp->cep  = 0;                     /* Fill first and only entry in CAM */
  cdp->cap0 = hwaddr[1] << 8 | hwaddr[0];
  cdp->cap1 = hwaddr[3] << 8 | hwaddr[2];
  cdp->cap2 = hwaddr[5] << 8 | hwaddr[4];
  cdp->ce   = 0x0001;                /* Enable first entry in CAM */

  (*sc->write_register)( rp, SONIC_REG_CDC, 1 );      /* 1 entry in CDA */
  (*sc->write_register)( rp, SONIC_REG_CDP, LSW(cdp) );
  (*sc->write_register)( rp, SONIC_REG_CR,  CR_LCAM );  /* Load the CAM */

  while ((*sc->read_register)( rp, SONIC_REG_CR ) & CR_LCAM)
    continue;

  /*
   * Verify that CAM was properly loaded.
   */

  (*sc->write_register)( rp, SONIC_REG_CR, CR_RST | CR_STP | CR_RXDIS | CR_HTX );

#if (SONIC_DEBUG & SONIC_DEBUG_CAM)
  (*sc->write_register)( rp, SONIC_REG_CEP, 0 );  /* Select first entry in CAM */
    printf ("Loaded Ethernet address into SONIC CAM.\n"
      "  Wrote %04x%04x%04x - %#x\n"
      "   Read %04x%04x%04x - %#x\n", 
        cdp->cap2, cdp->cap1, cdp->cap0, cdp->ce,
        (*sc->read_register)( rp, SONIC_REG_CAP2 ),
        (*sc->read_register)( rp, SONIC_REG_CAP1 ),
        (*sc->read_register)( rp, SONIC_REG_CAP0 ),
        (*sc->read_register)( rp, SONIC_REG_CE ));

  (*sc->write_register)( rp, SONIC_REG_CEP, 0 );  /* Select first entry in CAM */
  if (((*sc->read_register)( rp, SONIC_REG_CAP2 ) != cdp->cap2)
   || ((*sc->read_register)( rp, SONIC_REG_CAP1 ) != cdp->cap1)
   || ((*sc->read_register)( rp, SONIC_REG_CAP0 ) != cdp->cap0)
   || ((*sc->read_register)( rp, SONIC_REG_CE ) != cdp->ce)) {
    printf ("Failed to load Ethernet address into SONIC CAM.\n"
      "  Wrote %04x%04x%04x - %#x\n"
      "   Read %04x%04x%04x - %#x\n", 
        cdp->cap2, cdp->cap1, cdp->cap0, cdp->ce,
        (*sc->read_register)( rp, SONIC_REG_CAP2 ),
        (*sc->read_register)( rp, SONIC_REG_CAP1 ),
        (*sc->read_register)( rp, SONIC_REG_CAP0 ),
        (*sc->read_register)( rp, SONIC_REG_CE ));
    rtems_panic ("SONIC LCAM");
  }
#endif

  (*sc->write_register)(rp, SONIC_REG_CR, /* CR_TXP | */CR_RXEN | CR_STP);

  /*
   * Attach SONIC interrupt handler
   */
/* XXX
  (*sc->write_register)( rp, SONIC_REG_IMR, 0 );
*/
  old_handler = set_vector(sonic_interrupt_handler, sc->vector, 1);

  /*
   * Remainder of hardware initialization is
   * done by the receive and transmit daemons.
   */
}

/*
 * Send packet (caller provides header).
 */

SONIC_STATIC void sonic_start(struct ifnet *ifp)
{
  struct sonic_softc *sc = ifp->if_softc;

  rtems_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT);
  ifp->if_flags |= IFF_OACTIVE;
}

/*
 * Initialize and start the device
 */

SONIC_STATIC void sonic_init (void *arg)
{
  struct sonic_softc *sc = arg;
  struct ifnet *ifp = &sc->arpcom.ac_if;
  void *rp = sc->sonic;
  int rcr;

  if (sc->txDaemonTid == 0) {

    /*
     * Set up SONIC hardware
     */
    sonic_initialize_hardware (sc);

    /*
     * Start driver tasks
     */
    sc->rxDaemonTid = rtems_bsdnet_newproc ("SNrx", 4096, sonic_rxDaemon, sc);
    sc->txDaemonTid = rtems_bsdnet_newproc ("SNtx", 4096, sonic_txDaemon, sc);
  }

  /*
   * Set flags appropriately
   */
  rcr = (*sc->read_register)( rp, SONIC_REG_RCR );
  if (ifp->if_flags & IFF_PROMISC)
    rcr |= RCR_PRO;
  else
    rcr &= ~RCR_PRO;
  (*sc->write_register)( rp, SONIC_REG_RCR, rcr);

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

  /*
   * Enable receiver and transmitter
   */
  sonic_enable_interrupts( sc, IMR_TXEREN | (IMR_PRXEN | IMR_RBAEEN) );
  sonic_command( sc, CR_RXEN );
}

/*
 * Driver ioctl handler
 */
static int
sonic_ioctl (struct ifnet *ifp, int command, caddr_t data)
{
  struct sonic_softc *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:
          sonic_stop (sc);
          break;

        case IFF_UP:
          sonic_init (sc);
          break;

        case IFF_UP | IFF_RUNNING:
          sonic_stop (sc);
          sonic_init (sc);
          break;

        default:
          break;
        }
      break;

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

/*
 * Attach an SONIC driver to the system
 * This is the only `extern' function in the driver.
 */

int
rtems_sonic_driver_attach (
  struct rtems_bsdnet_ifconfig *config,
  sonic_configuration_t *chip
)
{
  struct sonic_softc *sc;
  struct ifnet *ifp;
  int mtu;
  int unitNumber;
  char *unitName;

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

  /*
   * Is driver free?
   */
  if ((unitNumber <= 0) || (unitNumber > NSONIC)) {
    printf ("Bad SONIC unit number.\n");
     return 0;
  }
  sc = &sonic_softc[unitNumber - 1];
  ifp = &sc->arpcom.ac_if;
  if (ifp->if_softc != NULL) {
    printf ("Driver already in use.\n");
    return 0;
  }

  /*
   *  zero out the control structure
   */

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


  /*
   * Process options
   */
  if (config->hardware_address) {
    memcpy (sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
  }
  else {
    memset (sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN);
  }
  if (config->mtu)
    mtu = config->mtu;
  else
    mtu = ETHERMTU;
  if (config->rbuf_count)
    sc->rdaCount = config->rbuf_count;
  else
    sc->rdaCount = chip->rda_count;
  if (config->xbuf_count)
    sc->tdaCount = config->xbuf_count;
  else
    sc->tdaCount = chip->tda_count;
  sc->acceptBroadcast = !config->ignore_broadcast;

  sc->sonic = (void *) chip->base_address;
  sc->vector = chip->vector;
  sc->dcr_value = chip->dcr_value;
  sc->dc2_value  = chip->dc2_value;
  sc->write_register = chip->write_register;
  sc->read_register  = chip->read_register;

  /*
   * Set up network interface values
   */
  ifp->if_softc = sc;
  ifp->if_unit = unitNumber;
  ifp->if_name = unitName;
  ifp->if_mtu = mtu;
  ifp->if_init = sonic_init;
  ifp->if_ioctl = sonic_ioctl;
  ifp->if_start = sonic_start;
  ifp->if_output = ether_output;
  ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;
  if (ifp->if_snd.ifq_maxlen == 0)
    ifp->if_snd.ifq_maxlen = ifqmaxlen;

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

#if (SONIC_DEBUG & SONIC_DEBUG_PRINT_REGISTERS)
#include <stdio.h>

char SONIC_Reg_name[64][6]= {
    "CR",         /* 0x00 */
    "DCR",        /* 0x01 */
    "RCR",        /* 0x02 */
    "TCR",        /* 0x03 */
    "IMR",        /* 0x04 */
    "ISR",        /* 0x05 */
    "UTDA",       /* 0x06 */
    "CTDA",       /* 0x07 */
    "0x08",       /* 0x08 */
    "0x09",       /* 0x09 */
    "0x0A",       /* 0x0A */
    "0x0B",       /* 0x0B */
    "0x0C",       /* 0x0C */
    "URDA",       /* 0x0D */
    "CRDA",       /* 0x0E */
    "0x0F",       /* 0x0F */
    "0x10",       /* 0x10 */
    "0x11",       /* 0x11 */
    "0x12",       /* 0x12 */
    "EOBC",       /* 0x13 */
    "URRA",       /* 0x14 */
    "RSA",        /* 0x15 */
    "REA",        /* 0x16 */
    "RRP",        /* 0x17 */
    "RWP",        /* 0x18 */
    "0x19",       /* 0x19 */
    "0x1A",       /* 0x1A */
    "0x1B",       /* 0x1B */
    "0x1C",       /* 0x1C */
    "0x0D",       /* 0x1D */
    "0x1E",       /* 0x1E */
    "0x1F",       /* 0x1F */
    "0x20",       /* 0x20 */
    "CEP",        /* 0x21 */
    "CAP2",       /* 0x22 */
    "CAP1",       /* 0x23 */
    "CAP0",       /* 0x24 */
    "CE",         /* 0x25 */
    "CDP",        /* 0x26 */
    "CDC",        /* 0x27 */
    "SR",         /* 0x28 */
    "WT0",        /* 0x29 */
    "WT1",        /* 0x2A */
    "RSC",        /* 0x2B */
    "CRCT",       /* 0x2C */
    "FAET",       /* 0x2D */
    "MPT",        /* 0x2E */
    "MDT",        /* 0x2F */
    "0x30",       /* 0x30 */
    "0x31",       /* 0x31 */
    "0x32",       /* 0x32 */
    "0x33",       /* 0x33 */
    "0x34",       /* 0x34 */
    "0x35",       /* 0x35 */
    "0x36",       /* 0x36 */
    "0x37",       /* 0x37 */
    "0x38",       /* 0x38 */
    "0x39",       /* 0x39 */
    "0x3A",       /* 0x3A */
    "0x3B",       /* 0x3B */
    "0x3C",       /* 0x3C */
    "0x3D",       /* 0x3D */
    "0x3E",       /* 0x3E */
    "DCR2"        /* 0x3F */
};
#endif