www.pudn.com > bluecat_cs8900a.rar > bluecat_cs8900a.c


/* Created by ISDCorp 1999 www.isdcorp.com */ 
/* cs8900a.c: A Crystal Semiconductor CS89[02]0 driver for linux on 
*     EP7312 platform 
* 
*Based on cs89x0.c 
*/ 
 
static char *version = 
"cs8900.c:v0.01 based on v1.02 Russell Nelson \n"; 
 
/* use 0 for production, 1 for verification, >2 for debug */ 
#ifndef NET_DEBUG 
#define NET_DEBUG 6 
#endif 
 
#ifdef MODULE 
#include  
#include  
#else 
#define MOD_INC_USE_COUNT 
#define MOD_DEC_USE_COUNT 
#endif 
 
#define PRINTK(x) printk x 
 
extern intnfsdebugon; 
 
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
 
#include  
 
#include  
#include  
#include  
#include  
#include "cs8900a.h" 
#include  
 
/* 
* Locally used declarations 
*/ 
#define NETCARD_IO_EXTENT       16 
#define tx_done(dev) 1 
 
#define EPORT_ADDR      0x300 
#define EIOADDR         (EIO_BASE+EPORT_ADDR) 
 
#define __EIOB(x)       (*(volatile unsigned char *)  (EIOADDR+(x))) 
#define __EIOW(x)       (*(volatile uint32_t      *)  (EIOADDR+(x))) 
 
#undef TX_CMD_PORT 
#undef TX_LEN_PORT 
#undef ISQ_PORT 
#undef ADD_PORT 
#undef DATA_PORT 
#undef DATA32_PORT 
 
#if defined(CONFIG_ARCH_CLEE89712) || defined(CONFIG_ARCH_CLEP7312) 
#define TX_CMD_PORT     0x0004 
#define TX_LEN_PORT     0x0006 
#define ISQ_PORT        0x0008 
#define ADD_PORT        0x000A 
#define DATA_PORT       0x000C 
#define DATA32_PORT     0x000E 
#elif defined(CONFIG_ARCH_CLEP7212) 
#define TX_CMD_PORT     0x0008 
#define TX_LEN_PORT     0x000c 
#define ISQ_PORT        0x0010 
#define ADD_PORT        0x0014 
#define DATA_PORT       0x0018 
#define DATA32_PORT     0x001c 
#else 
#error "Unsupported architecture" 
#endif 
 
/* Information that need to be kept for each board. */ 
struct net_local { 
   struct net_device_stats stats; 
   int chip_type;/* one of: CS8900, CS8920, CS8920M */ 
   char chip_revision;/* revision letter of the chip ('A'...) */ 
   int send_cmd;/* the propercommand used to send a packet. */ 
   int auto_neg_cnf; 
   int adapter_cnf; 
   int isa_config; 
   int irq_map; 
   int rx_mode; 
   int curr_rx_cfg; 
   int linectl; 
   int send_underrun;      /* keep track of how many UE we get */ 
   struct sk_buff *skb; 
}; 
 
extern int    cs89x0_probe(struct device *dev); 
static int    cs89x0_probe1(struct device *dev, int ioaddr); 
static int    net_open(struct device *dev); 
static int    net_send_packet(struct sk_buff *skb, struct device *dev); 
static void   net_interrupt(int irq, void *dev_id, struct pt_regs *regs); 
static void   set_multicast_list(struct device *dev); 
static void   net_rx(struct device *dev); 
static int    net_close(struct device *dev); 
static struct net_device_stats *net_get_stats(struct device *dev); 
static void   reset_chip(struct device *dev); 
static int    set_mac_address(struct device *dev, void *addr); 
static int    get_eeprom_data(struct device *dev, int off, int len,int *buffer); 
static int    get_eeprom_cksum(int off, int len, int *buffer); 
static int    inline readreg(struct device *dev, int portno); 
void      cs_set_cnf( struct device *dev, struct net_local *lp ); 
static void   inline writereg(struct device *dev, int portno, int value); 
extern void   enable_irq(unsigned int irq); 
static uint16_t inline readword(struct device *dev, int portno); 
void      cs_ins(int port, uint16_t *to, int len ); 
void      cs_outs(int port, uint16_t *from, int len ); 
 
#ifdef SET_NET_DEBUG 
static void   printpkt( unsigned char *bptr, int len ); 
#endif 
 
#ifdef __bluecat__ 
char        macaddr[6] = { 0x00, 0xe0, 0x98, 0x06, 0x91, 0x66 }; 
static int  macaddr_changed = 0; 
 
void cs8900a_setup( char * str, int * ints ) 
{ 
	int  i; 
	char ch, *pos; 
	u8   byte = 0; 
	char addr[6]; 
	 
	for( pos=str, i=0; *pos; pos++ ) { 
		ch = *pos; 
	 
	if( ch == ':' ) { 
		if( i++ < 6 ) { 
			byte = 0; 
			continue; 
		} else { 
			printk("Bad MAC address: '%s' - ignored.\n", str); 
			return; 
		} 
	} else { 
		byte <<= 4; 
		if( (ch >= '0') && (ch <= '9') ) { 
			byte += ch - '0'; 
		} else if( (ch >= 'a') && (ch <= 'f') ) { 
			byte += ch - 'a' + 10; 
		} else if( (ch >= 'A') && (ch <= 'F') ) { 
			byte += ch - 'F' + 10; 
		} else { 
			printk("Bad MAC address: '%s' - ignored.\n", str); 
			return; 
		} 
		addr[i] = byte; 
	} 
} 
 
for (i = 0; i < 6; i++) 
	macaddr[i] = addr[i]; 
	macaddr_changed = 1; 
} 
#endif 
 
/* 
* Variables used by the driver 
*/ 
static unsigned int net_debug = 0; 
 
__initfunc(int 
cs89x0_probe(struct device *dev)) 
{ 
   int base_addr = dev ? dev->base_addr : 0; 
 
   if (base_addr > 0x1ff)/* Check a single specified location. */ 
   return cs89x0_probe1(dev, base_addr); 
   else if (base_addr != 0)/* Don't probe at all. */ 
   return ENXIO; 
 
   dev->base_addr = EPORT_ADDR; 
 
   if (cs89x0_probe1(dev, EPORT_ADDR) == 0) 
   return 0; 
 
   printk("cs89x0: no cs8900 or cs8920 detected.\n"); 
   printk("Be sure to disable PnP with SETUP\n"); 
   return ENODEV; 
} 
 
/* This is the real probe routine.  Linux has a history of friendly device 
  probes on the ISA bus.  A good device probes avoids doing writes, and 
  verifies that the correct device exists and functions.  */ 
 
__initfunc(static int cs89x0_probe1(struct device *dev, int ioaddr)) 
{ 
   struct net_local *lp; 
   static unsigned version_printed = 0; 
   int i; 
   unsigned rev_type = 0; 
   int eeprom_buff[CHKSUM_LEN]; 
 
   /* Initialize the device structure. */ 
   if (dev->priv == NULL) { 
   dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); 
       memset(dev->priv, 0, sizeof(struct net_local)); 
   } 
 
   lp = (struct net_local *)dev->priv; 
 
   /* Fill in the 'dev' fields. */ 
   dev->base_addr = EPORT_ADDR; 
 
   /* get the chip type */ 
   rev_type = readreg(dev, PRODUCT_ID_ADD); 
   lp->chip_type = rev_type &~ REVISON_BITS; 
   lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; 
 
   /* Check the chip type and revision in order to set correct send command 
      CS8920 revision C and CS8900 revision F can use the faster send. */ 
   lp->send_cmd = TX_AFTER_381; 
   if (lp->chip_type == CS8900 && lp->chip_revision >= 'F') 
   lp->send_cmd = TX_NOW; 
 
   if (net_debug  &&  version_printed++ == 0) 
   printk(version); 
 
   printk("%s: cs89%c0%s rev %c found at %#3lx", 
          dev->name, 
          lp->chip_type==CS8900?'0':'2', 
          lp->chip_type==CS8920M?"M":"", 
          lp->chip_revision, 
          dev->base_addr); 
 
   reset_chip(dev); 
 
   /* First check to see if an EEPROM is attached*/ 
   if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0) { 
   printk("\ncs89x0: No EEPROM, using default setup temporarily.\n"); 
   cs_set_cnf( dev, lp ); 
   }else if (get_eeprom_data(dev,START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff)<0) { 
   printk("\ncs89x0: EEPROM read failed, using default setup temporarily.\n"); 
   cs_set_cnf( dev, lp ); 
   }else if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0)  { 
       printk("\ncs89x0: EEPROM checksum bad, using default setup temporarily.\n"); 
       cs_set_cnf( dev, lp ); 
   }else { 
       /* get transmission control word  but keep the autonegotiation bits */ 
       if (!lp->auto_neg_cnf) 
   lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2]; 
 
       /* Store adapter configuration */ 
       if (!lp->adapter_cnf) 
   lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2]; 
 
       /* Store ISA configuration */ 
       lp->isa_config = eeprom_buff[ISA_CNF_OFFSET/2]; 
 
       /* store the initial memory base address */ 
       dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8; 
 
#ifdef __bluecat__ 
if (macaddr_changed) 
 	memcpy (dev->dev_addr, macaddr, sizeof (macaddr)); 
else 
#endif 
       for (i = 0; i < ETH_ALEN/2; i++) { 
            dev->dev_addr[i*2] = eeprom_buff[i]; 
            dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8; 
        } 
   } 
 
   printk(" media %s%s%s", 
          (lp->adapter_cnf & A_CNF_10B_T)?"RJ-45,":"", 
          (lp->adapter_cnf & A_CNF_AUI)?"AUI,":"", 
          (lp->adapter_cnf & A_CNF_10B_2)?"BNC,":""); 
 
   lp->irq_map = 0xffff; 
   dev->irq = IRQ_EINT3; 
 
   printk(" IRQ %d, mac = ", dev->irq); 
 
   writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON); 
   writereg(dev, PP_CS8900_ISAINT, 0); 
   if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) { 
	printk("%s: IRQ conflict?\n", dev->name); 
   return -EAGAIN; 
   } 
 
   /* print the ethernet address. */ 
   for (i = 0; i < ETH_ALEN; i++) 
   printk(" %2.2x", dev->dev_addr[i]); 
 
   /* Grab the region so we can find another board if autoIRQ fails. */ 
   request_region(EPORT_ADDR, NETCARD_IO_EXTENT,"cs89x0"); 
 
   dev->open = net_open; 
   dev->stop = net_close; 
   dev->hard_start_xmit = net_send_packet; 
   dev->get_stats = net_get_stats; 
   dev->set_multicast_list = &set_multicast_list; 
   dev->set_mac_address = &set_mac_address; 
 
   /* Fill in the fields of the device structure with ethernet values. */ 
   ether_setup(dev); 
 
#ifndef __bluecat__ 
   printk(" probe1end...\n"); 
#else 
   printk("\n"); 
#endif 
 
   /* Enable interrupts*/ 
//    writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON); 
//    writereg(dev, PP_CS8900_ISAINT, 0); 
   enable_irq(IRQ_EINT3); 
 
   return 0; 
} 
 
void 
cs_set_cnf( struct device *dev, struct net_local *lp ) 
{ 
#ifndef __bluecat__ 
   char        macaddr[6] = { 0x00, 0xe0, 0x98, 0x06, 0x91, 0x66 }; 
#endif     
   int         i; 
 
   if (!lp->auto_neg_cnf) 
       lp->auto_neg_cnf = 1; 
 
   /* Store adapter configuration */ 
   lp->adapter_cnf =  A_CNF_MEDIA_10B_T|A_CNF_10B_T|A_CNF_DC_DC_POLARITY; 
   lp->isa_config = 0; 
   dev->mem_start = EIOADDR; 
   for (i = 0; i < ETH_ALEN; i++) 
       dev->dev_addr[i] = macaddr[i]; 
} 
 
__initfunc(void 
reset_chip(struct device *dev)) 
{ 
   int reset_start_time; 
 
   writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); 
 
   /* wait 30 ms */ 
   current->state = TASK_INTERRUPTIBLE; 
   schedule_timeout(30*HZ/1000); 
 
   /* Wait until the chip is reset */ 
   reset_start_time = jiffies; 
   while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && 
   jiffies - reset_start_time < 2 ) 
   ; 
} 
 
static void 
control_dc_dc(struct device *dev, int on_not_off) 
{ 
   struct net_local *lp = (struct net_local *)dev->priv; 
   unsigned int selfcontrol; 
   int timenow = jiffies; 
   /* control the DC to DC convertor in the SelfControl register.  */ 
 
   selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */ 
   if (((lp->adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off) 
   selfcontrol |= HCB1; 
   else 
   selfcontrol &= ~HCB1; 
   writereg(dev, PP_SelfCTL, selfcontrol); 
 
   /* Wait for the DC/DC converter to power up - 500ms */ 
   while (jiffies - timenow < 100) 
   ; 
 
} 
 
static int 
detect_tp(struct device *dev) 
{ 
   struct net_local *lp = (struct net_local *)dev->priv; 
   int timenow = jiffies; 
 
   if (net_debug > 1) 
printk("%s: Attempting TP\n", dev->name); 
 
   /* If connected to another full duplex capable, 10-Base-T card link pulses 
    * seem to be lost when the auto detect bit in the LineCTL is set. 
    * To overcome this, auto detect bit will be cleared whilst testing the 
    * 10-Base-T interface. This would not be necessary for the sparrow chip but 
    * is simpler to do it anyway. 
    */ 
   writereg(dev, PP_LineCTL, lp->linectl &~ AUI_ONLY); 
   control_dc_dc(dev, 0); 
 
   /* Delay for the hardware to work out if the TP cable is present - 150ms */ 
   for (timenow = jiffies; jiffies - timenow < 15; ) 
               ; 
   if ((readreg(dev, PP_LineST) & LINK_OK) == 0) 
   return 0; 
 
   return A_CNF_MEDIA_10B_T; 
} 
 
/* Open/initialize the board.  This is called (in the current kernel) 
* sometime after booting when the 'ifconfig' program is run. 
* 
* This routine should set everything up anew at each open, even 
* registers that "should" only need to be set once at boot, so that 
* there is non-reboot way to recover if something goes wrong. 
*/ 
static int 
net_open(struct device *dev) 
{ 
   struct net_local *lp = (struct net_local *)dev->priv; 
   int result = 0; 
   int i; 
 
   DEBUGNET2("cyn: net_open flags=0x%x type=0x%x\n", dev->flags, dev->type); 
 
   /* set the Ethernet address */ 
   for (i=0; i < ETH_ALEN/2; i++) 
   writereg(dev, PP_IA+i*2, 
dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); 
 
   /* while we're testing the interface, leave interrupts disabled */ 
   writereg(dev, PP_BusCTL, MEMORY_ON); 
 
   /* Set LineCTL quintuplet based on adapter configuration read from EEPROM */ 
   if ( (lp->adapter_cnf & A_CNF_EXTND_10B_2) && 
(lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH) ) 
       lp->linectl = LOW_RX_SQUELCH; 
   else 
       lp->linectl = 0; 
 
   result = detect_tp(dev); 
   if (!result) 
printk("%s: 10Base-T (RJ-45) has no cable\n", dev->name); 
 
   /* check "ignore missing media" bit */ 
   if (lp->auto_neg_cnf & IMM_BIT) 
/* Yes! I don't care if I see a link pulse */ 
       result = A_CNF_MEDIA_10B_T; 
 
   if ( result == A_CNF_MEDIA_10B_T ) 
       printk("%s: using 10Base-T (RJ-45)\n", dev->name); 
   else { 
       printk("%s: no network cable attached to configured media\n", 
dev->name); 
writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) 
& ~(SERIAL_TX_ON | SERIAL_RX_ON)); 
       return -EAGAIN; 
   } 
 
   /* Turn on both receive and transmit operations */ 
   writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | 
SERIAL_RX_ON | SERIAL_TX_ON); 
 
   /* Receive only error free packets addressed to this card */ 
   lp->rx_mode = 0; 
   writereg(dev, PP_RxCTL, DEF_RX_ACCEPT); 
 
   lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; 
   if (lp->isa_config & STREAM_TRANSFER) 
   lp->curr_rx_cfg |= RX_STREAM_ENBL; 
 
   writereg(dev, PP_RxCFG, lp->curr_rx_cfg); 
 
   writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | 
          TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); 
 
   writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | 
    TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL); 
 
   /* now that we've got our act together, enable everything */ 
   writereg(dev, PP_BusCTL, ENABLE_IRQ); 
   dev->tbusy = 0; 
   dev->interrupt = 0; 
   dev->start = 1; 
   MOD_INC_USE_COUNT; 
   return 0; 
} 
 
static int 
net_send_packet(struct sk_buff *skb, struct device *dev) 
{ 
   interror=0; 
 
//printk("net_send_packet..%d\n", skb->len); 
   DEBUGNET("cyn: net_send_packet "); 
   if (dev->tbusy) { 
   /* If we get here, some higher level has decided we are broken. 
      There should really be a "kick me" function call instead. */ 
   int tickssofar = jiffies - dev->trans_start; 
   if (tickssofar < 5) 
       return 1; 
   if (net_debug > 0) 
   printk("%s: transmit timed out, network cable problem?\n", 
dev->name); 
   /* Try to restart the adaptor. */ 
   dev->tbusy=0; 
   dev->trans_start = jiffies; 
   error++; 
   } 
 
   /* Block a timer-based transmit from overlapping.  This could better be 
      done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ 
   if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) 
   printk("%s: Transmitter access conflict.\n", dev->name); 
   else { 
   struct net_local *lp = (struct net_local *)dev->priv; 
   unsigned long flags; 
 
   if (net_debug > 3) 
   printk("%s: sent %d byte packet of type %x\n", dev->name, skb->len, 
 (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]); 
 
#ifdef SET_NET_DEBUG 
   if ( (skb->data[ETH_ALEN+ETH_ALEN+22] == 8  && 
         skb->data[ETH_ALEN+ETH_ALEN+23] == 1) || 
        (skb->data[ETH_ALEN+ETH_ALEN+24] == 8  && 
         skb->data[ETH_ALEN+ETH_ALEN+25] == 1) ) { 
   DEBUGNET2("cyn: net_send_packet %d byte, type %x\n", skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]); 
   printk("cyn: net_send_packet %d byte, type %x\n", skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]); 
   if ( skb->len > 34 ) 
   printpkt(skb->data+34, skb->len-34); 
} 
#endif 
   /* keep the upload from being interrupted, since we 
        * ask the chip to start transmitting before the 
        * whole packet has been completely uploaded. 
*/ 
   save_flags(flags); 
   cli(); 
 
   /* initiate a transmit sequence */ 
   __EIOW(TX_CMD_PORT) = lp->send_cmd; 
   __EIOW(TX_LEN_PORT) = skb->len; 
 
   /* Test to see if the chip has allocated memory for the packet */ 
   if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { 
       /* Gasp!  It hasn't.  But that shouldn't happen since 
        * we're waiting for TxOk, so return 1 and requeue this packet. 
    */ 
       restore_flags(flags); 
       return 1; 
   } 
 
   /* Write the contents of the packet */ 
       cs_outs(TX_FRAME_PORT,(unsigned short *)skb->data,(skb->len+1) >>1); 
 
   restore_flags(flags); 
   dev->trans_start = jiffies; 
   } 
   dev_kfree_skb (skb); 
 
   return 0; 
} 
 
/* The typical workload of the driver: 
  Handle the network interface interrupts. */ 
static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs) 
{ 
   struct device *dev = dev_id; 
   struct net_local *lp; 
   unsigned int ioaddr, status; 
 
   DEBUGNET("cyn: net_interrupt "); 
   if (dev == NULL) { 
   printk ("net_interrupt(): irq %d for unknown device.\n", irq); 
   return; 
   } 
   if (dev->interrupt) 
   printk("%s: Re-entering the interrupt handler.\n", dev->name); 
   dev->interrupt = 1; 
 
   ioaddr = dev->base_addr; 
   lp = (struct net_local *)dev->priv; 
 
   /* we MUST read all the events out of the ISQ, otherwise we'll never 
    * get interrupted again.  As a consequence, we can't have any limit 
    * on the number of times we loop in the interrupt handler.  The 
    * hardware guarantees that eventually we'll run out of events.  Of 
    * course, if you're on a slow machine, and packets are arriving 
    * faster than you can read them off, you're screwed.  Hasta la 
    * vista, baby!  
    */ 
   while ((status = readword(dev, ISQ_PORT))) { 
   if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status); 
   switch(status & ISQ_EVENT_MASK) { 
   case ISQ_RECEIVER_EVENT: 
           DEBUGNET("rcv "); 
       /* Got a packet(s). */ 
       net_rx(dev); 
       break; 
 
   case ISQ_TRANSMITTER_EVENT: 
       lp->stats.tx_packets++; 
       dev->tbusy = 0; 
       mark_bh(NET_BH);/* Inform upper layers. */ 
       if ((status & TX_OK) == 0) 
lp->stats.tx_errors++; 
       if (status & TX_LOST_CRS) 
lp->stats.tx_carrier_errors++; 
       if (status & TX_SQE_ERROR) 
lp->stats.tx_heartbeat_errors++; 
       if (status & TX_LATE_COL) 
lp->stats.tx_window_errors++; 
       if (status & TX_16_COL) 
lp->stats.tx_aborted_errors++; 
           DEBUGNET2("xmit status=0x%x npkt=%d", status, lp->stats.tx_packets); 
   break; 
 
   case ISQ_BUFFER_EVENT: 
          DEBUGNET("buffer "); 
      if (status & READY_FOR_TX) { 
   /* we tried to transmit a packet earlier, 
                * but inexplicably ran out of buffers. 
                * That shouldn't happen since we only ever 
                * load one packet.  Shrug.  Do the right 
                * thing anyway. 
*/ 
   dev->tbusy = 0; 
   mark_bh(NET_BH);/* Inform upper layers. */ 
       } 
       if (status & TX_UNDERRUN) { 
   if (net_debug > 0) 
printk("%s: transmit underrun\n", dev->name); 
               lp->send_underrun++; 
               if (lp->send_underrun == 3) 
   lp->send_cmd = TX_AFTER_381; 
               else if 
   (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL; 
#ifdef __bluecat__ 
/* transmit cycle is done, although 
  frame wasn't transmitted - this 
  avoids having to wait for the upper 
  layers to timeout on us, in the 
  event of a tx underrun */ 
dev->tbusy = 0; 
mark_bh(NET_BH);/* Inform upper layers. */ 
#endif 
           } 
       break; 
 
   case ISQ_RX_MISS_EVENT: 
   DEBUGNET("miss "); 
       lp->stats.rx_missed_errors += (status >>6); 
       break; 
 
   case ISQ_TX_COL_EVENT: 
   DEBUGNET("coll "); 
       lp->stats.collisions += (status >>6); 
       break; 
   } 
   } 
   dev->interrupt = 0; 
/* 
printk(" netintexit...\n"); 
*/ 
   return; 
} 
 
/* We have a good packet(s), get it/them out of the buffers. */ 
static void 
net_rx(struct device *dev) 
{ 
   struct net_local *lp = (struct net_local *)dev->priv; 
   struct sk_buff *skb; 
   uint16_t status, length; 
 
   status = __EIOW(RX_FRAME_PORT); 
   length = __EIOW(RX_FRAME_PORT); 
   DEBUGNET2(" status=0x%x, len=0x%x ", status, length); 
 
   if ((status & RX_OK) == 0) { 
   lp->stats.rx_errors++; 
   if (status & RX_RUNT) 
   lp->stats.rx_length_errors++; 
   if (status & RX_EXTRA_DATA) 
   lp->stats.rx_length_errors++; 
   if (status & RX_CRC_ERROR) 
   if (!(status & (RX_EXTRA_DATA|RX_RUNT))) 
   /* per str 172 */ 
   lp->stats.rx_crc_errors++; 
   if (status & RX_DRIBBLE) 
   lp->stats.rx_frame_errors++; 
   return; 
   } 
 
   /* Malloc up new buffer. */ 
#ifdef __bluecat__ 
   skb = alloc_skb(length+2, GFP_ATOMIC); 
#else     
   skb = alloc_skb(length, GFP_ATOMIC); 
#endif     
   if (skb == NULL) { 
   printk("%s: Memory squeeze, dropping packet.\n", dev->name); 
   lp->stats.rx_dropped++; 
   return; 
   } 
   skb->len = length; 
   skb->dev = dev; 
 
#ifdef __bluecat__ 
   skb_reserve(skb, 2);/* Align IP on 16 byte boundaries */ 
#endif     
 
   cs_ins(RX_FRAME_PORT, (unsigned short *)skb->data, length >> 1); 
 
   if (length & 1) 
   skb->data[length-1] = __EIOW(RX_FRAME_PORT); 
 
   if (net_debug > 3) 
printk("%s: received %d byte packet of type %x\n", dev->name, length, 
         (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]); 
 
   skb->protocol=eth_type_trans(skb,dev); 
 
#ifdef SET_NET_DEBUG 
   if ( (skb->data[20] == 8  && skb->data[21] == 1) || 
    (skb->data[22] == 8  && skb->data[23] == 1) ) { 
DEBUGNET2("cyn: net_rcv len=%d, proto=0x%x\n", skb->len, skb->protocol); 
printk("cyn: net_rcv len=%d, proto=0x%x\n", skb->len, skb->protocol); 
if ( skb->len > 20 ) 
   printpkt(skb->data+20, skb->len-20); 
   } 
#endif 
 
//fc99    if ( skb->data[ETH_ALEN+ETH_ALEN+1] != 6 ) 
#ifndef __bluecat__ 
       netif_rx(skb); 
#endif 
   lp->stats.rx_packets++; 
   lp->stats.rx_bytes+=skb->len; 
#ifdef __bluecat__ 
       netif_rx(skb); 
#endif 
//printk("net_rx %d...\n", skb->len); 
   return; 
} 
 
/* The inverse routine to net_open(). */ 
static int 
net_close(struct device *dev) 
{ 
 
   DEBUGNET("cyn: net_close\n"); 
   writereg(dev, PP_RxCFG, 0); 
   writereg(dev, PP_TxCFG, 0); 
   writereg(dev, PP_BufCFG, 0); 
   writereg(dev, PP_BusCTL, 0); 
 
   dev->start = 0; 
 
   /* Update the statistics here. */ 
 
   MOD_DEC_USE_COUNT; 
   return 0; 
 
} 
 
/* Get the current statistics.This may be called with the card open or 
  closed. */ 
static struct net_device_stats * 
net_get_stats(struct device *dev) 
{ 
   struct net_local *lp = (struct net_local *)dev->priv; 
 
   DEBUGNET("cyn: net_get_stats\n"); 
   cli(); 
   /* Update the statistics from the device registers. */ 
   lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6); 
   lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6); 
   sti(); 
 
   return &lp->stats; 
} 
 
static void set_multicast_list(struct device *dev) 
{ 
   struct net_local *lp = (struct net_local *)dev->priv; 
 
   DEBUGNET("cyn: net_multicast_list\n"); 
   if(dev->flags&IFF_PROMISC) 
   { 
   lp->rx_mode = RX_ALL_ACCEPT; 
   } 
   else if((dev->flags&IFF_ALLMULTI)||dev->mc_list) 
   { 
   /* The multicast-accept list is initialized to accept-all, and we 
      rely on higher-level filtering for now. */ 
   lp->rx_mode = RX_MULTCAST_ACCEPT; 
   }  
   else 
   lp->rx_mode = 0; 
 
   writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode); 
 
   /* in promiscuous mode, we accept errored packets, 
    * so we have to enable interrupts on them also 
    */ 
   writereg(dev, PP_RxCFG, lp->curr_rx_cfg | 
        (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL| 
 RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0)); 
} 
 
 
static int set_mac_address(struct device *dev, void *addr) 
{ 
   int i; 
 
   DEBUGNET("cyn: set_mac_address\n"); 
   if (dev->start) 
   return -EBUSY; 
   printk("%s: Setting MAC address to ", dev->name); 
   for (i = 0; i < 6; i++) 
   printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]); 
   printk(".\n"); 
   /* set the Ethernet address */ 
   for (i=0; i < ETH_ALEN/2; i++) 
   writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); 
 
   return 0; 
} 
 
static int inline 
readreg(struct device *dev, int portno) 
{ 
//fc99 #ifdef SET_NET_DEBUG 
#if 1 
   unsigned shortin; 
 
   DEBUGNET1("cyn: readreg port = 0x%x, ", portno); 
   __EIOW(ADD_PORT) = portno; 
   in = __EIOW(DATA_PORT); 
   DEBUGNET1("value = 0x%x\n", in); 
//printk("readreg port = 0x%x, value = 0x%x\n", portno, in); 
   return in; 
#else 
   __EIOW(ADD_PORT) = portno; 
   return __EIOW(DATA_PORT); 
#endif 
} 
 
static void inline 
writereg(struct device *dev, int portno, int value) 
{ 
 
   DEBUGNET2("cyn: writereg port = 0x%x, value=0x%x\n", portno, value); 
   __EIOW(ADD_PORT)  = portno; 
   __EIOW(DATA_PORT) = value; 
} 
 
 
static uint16_t inline 
readword(struct device *dev, int portno) 
{ 
   return __EIOW(portno); 
} 
 
static void inline 
writeword(struct device *dev, int portno, int value) 
{ 
   __EIOW(portno) = value; 
} 
 
__initfunc(static int 
wait_eeprom_ready(struct device *dev)) 
{ 
   int timeout = jiffies; 
 
   /* check to see if the EEPROM is ready, a timeout is used - 
    * just in case EEPROM is ready when SI_BUSY in the 
    * PP_SelfST is clear 
    */ 
   while(readreg(dev, PP_SelfST) & SI_BUSY) 
   if (jiffies - timeout >= 40) 
       return -1; 
   return 0; 
} 
 
__initfunc(static int 
get_eeprom_data(struct device *dev, int off, int len, int *buffer)) 
{ 
   int i; 
 
   if (net_debug > 3) 
printk("EEPROM data from %x for %x:\n",off,len); 
 
   for (i = 0; i < len; i++) { 
   if (wait_eeprom_ready(dev) < 0) 
   return -1; 
 
   /* Now send the EEPROM read command and EEPROM location to read */ 
   writereg(dev, PP_EECMD, (off + i) | EEPROM_READ_CMD); 
 
   if (wait_eeprom_ready(dev) < 0) 
   return -1; 
   buffer[i] = readreg(dev, PP_EEData); 
 
   if (net_debug > 3) 
   printk("%04x ", buffer[i]); 
   } 
 
   if (net_debug > 3) 
printk("\n"); 
 
   return 0; 
} 
 
__initfunc(static int 
get_eeprom_cksum(int off, int len, int *buffer)) 
{ 
   int i, cksum; 
 
   cksum = 0; 
   for (i = 0; i < len; i++) 
   cksum += buffer[i]; 
   cksum &= 0xffff; 
   if (cksum == 0) 
   return 0; 
   return -1; 
} 
 
void 
cs_ins(int port, uint16_t *to, int len ) 
{ 
   DEBUGNET1("cyn: cs_ins len=%d\n", len);  
 
   while ( len-- ) *to++ = __EIOW(port); 
} 
 
void 
cs_outs(int port, uint16_t *from, int len ) 
{ 
   DEBUGNET3("cyn: cs_outs p=0x%x, from=0x%x, len=%d\n", port, from, len); 
 
   while ( len-- ) __EIOW(port) = *from++;   
} 
 
#ifdef SET_NET_DEBUG 
static void 
printpkt( unsigned char *bptr, int len ) 
{ 
   static charhex[]= { '0','1','2','3','4','5','6','7','8', 
       '9','a','b','c','d','e','f' }; 
   charoutbuf[100]; 
   char*outp = outbuf; 
 
   if ( len > 72 ) 
       len = 72; 
 
   while ( len-- )  { 
       *outp++ = hex[(*bptr&0xf0)>>4] ; 
       *outp++ = hex[*bptr&0x0f] ; 
       bptr++; 
       if ( (len%24) == 0 ) { 
           *outp = 0; 
           printk("%s\n", outbuf);  
           outp = outbuf; 
       } else { 
           if ( (len%4) == 0 ) 
              *outp++ = ' '; 
           *outp++ = ' '; 
       } 
   } 
} 
#endif