📄 ax88796b.c
字号:
if (dev == NULL)
{
PRINTK (ERROR_MSG, "net_interrupt(): irq %d for unknown device.\n", irq);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
return;
#else
return 0;
#endif
}
writeb (E8390_NODMA+E8390_PAGE0, ax_base + E8390_CMD);
spin_lock_irqsave (&ax_local->page_lock, flags);
writeb (0x00, ax_base + EN0_IMR);
spin_unlock_irqrestore (&ax_local->page_lock, flags);
if (ax_local->irqlock) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
return;
#else
return 0;
#endif
}
spin_lock (&ax_local->page_lock);
while (1)
{
if ((interrupts = readb (ax_base + EN0_ISR)) == 0)
break;
writeb (interrupts, ax_base + EN0_ISR); /* Ack the interrupts */
if (interrupts & ENISR_TX)
ax_tx_intr (dev);
if (interrupts & ENISR_OVER)
ax_rx_overrun (dev);
if (interrupts & (ENISR_RX+ENISR_RX_ERR))
ax_receive (dev);
if (interrupts & ENISR_TX_ERR)
ax_tx_err (dev);
if (interrupts & ENISR_COUNTERS)
{
ax_local->stat.rx_frame_errors += readb (ax_base + EN0_COUNTER0);
ax_local->stat.rx_crc_errors += readb (ax_base + EN0_COUNTER1);
ax_local->stat.rx_missed_errors+= readb (ax_base + EN0_COUNTER2);
writeb (ENISR_COUNTERS, ax_base + EN0_ISR); /* Ack intr. */
}
if (interrupts & ENISR_RDC)
writeb (ENISR_RDC, ax_base + EN0_ISR);
writeb (E8390_NODMA+E8390_PAGE0+E8390_START, ax_base + E8390_CMD);
}
spin_unlock (&ax_local->page_lock);
spin_lock_irqsave (&ax_local->page_lock, flags);
writeb (ENISR_ALL, ax_base + EN0_IMR);
spin_unlock_irqrestore (&ax_local->page_lock, flags);
writeb (E8390_NODMA+E8390_PAGE0+E8390_START, ax_base + E8390_CMD);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
return;
#else
return IRQ_RETVAL (0);
#endif
}
/*
* ----------------------------------------------------------------------------
* Function Name: ax_tx_err
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
static void ax_tx_err (struct net_device *dev)
{
struct ax_device *ax_local = (struct ax_device *) dev->priv;
void *ax_base = ax_local->membase;
unsigned char txsr = readb (ax_base+EN0_TSR);
unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
if (tx_was_aborted)
ax_tx_intr (dev);
else
{
ax_local->stat.tx_errors++;
if (txsr & ENTSR_CRS) ax_local->stat.tx_carrier_errors++;
if (txsr & ENTSR_CDH) ax_local->stat.tx_heartbeat_errors++;
if (txsr & ENTSR_OWC) ax_local->stat.tx_window_errors++;
}
}
/*
* ----------------------------------------------------------------------------
* Function Name: ax_receive
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
static void ax_receive (struct net_device *dev)
{
struct ax_device *ax_local = (struct ax_device *) dev->priv;
void *ax_base = ax_local->membase;
unsigned char rxing_page, this_frame, next_frame;
unsigned short current_offset;
struct ax_pkt_hdr rx_frame;
int num_rx_pages = ax_local->stop_page - ax_local->rx_start_page;
while (1)
{
int pkt_len, pkt_stat;
/* Get the rx page (incoming packet pointer). */
writeb (E8390_NODMA+E8390_PAGE1, ax_base + E8390_CMD);
rxing_page = readb (ax_base + EN1_CURPAG);
writeb (E8390_NODMA+E8390_PAGE0, ax_base + E8390_CMD);
/* Remove one frame from the ring. Boundary is always a page behind. */
this_frame = readb (ax_base + EN0_BOUNDARY) + 1;
if (this_frame >= ax_local->stop_page)
this_frame = ax_local->rx_start_page;
if (this_frame != ax_local->current_page && (this_frame!=0x0 || rxing_page!=0xFF))
PRINTK (RX_MSG, "%s: mismatched read page pointers %2x vs %2x.\n",
dev->name, this_frame, ax_local->current_page);
if (this_frame == rxing_page) { /* Read all the frames? */
break; /* Done for now */
}
current_offset = this_frame << 8;
ax_get_hdr (dev, &rx_frame, this_frame);
pkt_len = rx_frame.count - sizeof (struct ax_pkt_hdr);
pkt_stat = rx_frame.status;
next_frame = this_frame + 1 + ((pkt_len+4)>>8);
/* Check for bogosity warned by 3c503 book: the status byte is never
written. This happened a lot during testing! This code should be
cleaned up someday. */
if (rx_frame.next != next_frame
&& rx_frame.next != next_frame + 1
&& rx_frame.next != next_frame - num_rx_pages
&& rx_frame.next != next_frame + 1 - num_rx_pages) {
ax_local->current_page = rxing_page;
writeb (ax_local->current_page-1, ax_base+EN0_BOUNDARY);
ax_local->stat.rx_errors++;
PRINTK (ERROR_MSG, "error occurred! Drop this packet!!\n");
continue;
}
if (pkt_len < 60 || pkt_len > 1518)
{
PRINTK (RX_MSG, "%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n",
dev->name, rx_frame.count, rx_frame.status,
rx_frame.next);
ax_local->stat.rx_errors++;
ax_local->stat.rx_length_errors++;
}
else if ((pkt_stat & 0x0F) == ENRSR_RXOK)
{
struct sk_buff *skb;
skb = dev_alloc_skb (pkt_len+2);
if (skb == NULL)
{
printk ("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len);
ax_local->stat.rx_dropped++;
break;
}
skb_reserve (skb, 2); /* IP headers on 16 byte boundaries */
skb->dev = dev;
skb_put (skb, pkt_len); /* Make room */
ax_block_input (dev, pkt_len, skb, current_offset + sizeof (rx_frame));
skb->protocol = eth_type_trans (skb,dev);
netif_rx (skb);
dev->last_rx = jiffies;
ax_local->stat.rx_packets++;
ax_local->stat.rx_bytes += pkt_len;
if (pkt_stat & ENRSR_PHY)
ax_local->stat.multicast++;
}
else
{
PRINTK (ERROR_MSG, "%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n",
dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
ax_local->stat.rx_errors++;
/* NB: The NIC counts CRC, frame and missed errors. */
if (pkt_stat & ENRSR_FO)
ax_local->stat.rx_fifo_errors++;
}
next_frame = rx_frame.next;
/* This _should_ never happen: it's here for avoiding bad clones. */
if (next_frame >= ax_local->stop_page) {
PRINTK (ERROR_MSG, "%s: next frame inconsistency, %#2x\n", dev->name, next_frame);
next_frame = ax_local->rx_start_page;
}
ax_local->current_page = next_frame;
writeb (next_frame-1, ax_base+EN0_BOUNDARY);
}
return;
}
/*
* ----------------------------------------------------------------------------
* Function Name: ax_rx_overrun
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
static void ax_rx_overrun (struct net_device *dev)
{
struct ax_device *ax_local = (struct ax_device *) dev->priv;
void *ax_base = ax_local->membase;
unsigned char was_txing, must_resend = 0;
/*
* Record whether a Tx was in progress and then issue the
* stop command.
*/
was_txing = readb (ax_base+E8390_CMD) & E8390_TRANS;
writeb (E8390_NODMA+E8390_PAGE0+E8390_STOP, ax_base+E8390_CMD);
PRINTK (RX_MSG, "%s: Receiver overrun.\n", dev->name);
ax_local->stat.rx_over_errors++;
udelay (2*1000);
writeb (0x00, ax_base+EN0_RCNTLO);
writeb (0x00, ax_base+EN0_RCNTHI);
/*
* See if any Tx was interrupted or not. According to NS, this
* step is vital, and skipping it will cause no end of havoc.
*/
if (was_txing)
{
unsigned char tx_completed = readb (ax_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR);
if (!tx_completed)
must_resend = 1;
}
/*
* Have to enter loopback mode and then restart the NIC before
* you are allowed to slurp packets up off the ring.
*/
writeb (E8390_TXOFF, ax_base + EN0_TXCR);
writeb (E8390_NODMA + E8390_PAGE0 + E8390_START, ax_base + E8390_CMD);
/*
* Clear the Rx ring of all the debris, and ack the interrupt.
*/
ax_receive (dev);
/*
* Leave loopback mode, and resend any packet that got stopped.
*/
writeb (E8390_TXCONFIG, ax_base + EN0_TXCR);
if (must_resend)
writeb (E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, ax_base + E8390_CMD);
}
/*
* Collect the stats. This is called unlocked and from several contexts.
*/
static struct net_device_stats *get_stats (struct net_device *dev)
{
struct ax_device *ax_local = (struct ax_device *) dev->priv;
void *ax_base = ax_local->membase;
unsigned long flags;
/* If the card is stopped, just return the present stats. */
if (!netif_running (dev))
return &ax_local->stat;
spin_lock_irqsave (&ax_local->page_lock,flags);
/* Read the counter registers, assuming we are in page 0. */
ax_local->stat.rx_frame_errors += readb (ax_base + EN0_COUNTER0);
ax_local->stat.rx_crc_errors += readb (ax_base + EN0_COUNTER1);
ax_local->stat.rx_missed_errors+= readb (ax_base + EN0_COUNTER2);
spin_unlock_irqrestore (&ax_local->page_lock, flags);
return &ax_local->stat;
}
/*
* ----------------------------------------------------------------------------
* Function Name: make_mc_bits
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
static inline void make_mc_bits (u8 *bits, struct net_device *dev)
{
struct dev_mc_list *dmi;
for (dmi=dev->mc_list; dmi; dmi=dmi->next)
{
u32 crc;
if (dmi->dmi_addrlen != ETH_ALEN)
{
PRINTK (INIT_MSG, "%s: invalid multicast address length given.\n", dev->name);
continue;
}
crc = ether_crc (ETH_ALEN, dmi->dmi_addr);
/*
* The 8390 uses the 6 most significant bits of the
* CRC to index the multicast table.
*/
bits[crc>>29] |= (1<<((crc>>26)&7));
}
}
/*
* ----------------------------------------------------------------------------
* Function Name: do_set_multicast_list
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
static void do_set_multicast_list (struct net_device *dev)
{
struct ax_device *ax_local = (struct ax_device *) dev->priv;
void *ax_base = ax_local->membase;
int i;
if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI)))
{
memset (ax_local->mcfilter, 0, 8);
if (dev->mc_list)
make_mc_bits (ax_local->mcfilter, dev);
}
else
memset (ax_local->mcfilter, 0xFF, 8); /* mcast set to accept-all */
if (netif_running (dev))
writeb (E8390_RXCONFIG, ax_base + EN0_RXCR);
writeb (E8390_NODMA + E8390_PAGE1, ax_base + E8390_CMD);
for (i = 0; i < 8; i++)
{
writeb (ax_local->mcfilter[i], ax_base + EN1_MULT_SHIFT (i));
}
writeb (E8390_NODMA + E8390_PAGE0, ax_base + E8390_CMD);
if (dev->flags&IFF_PROMISC)
writeb (E8390_RXCONFIG | 0x18, ax_base + EN0_RXCR);
else if (dev->flags&IFF_ALLMULTI || dev->mc_list)
writeb (E8390_RXCONFIG | 0x08, ax_base + EN0_RXCR);
else
writeb (E8390_RXCONFIG, ax_base + EN0_RXCR);
}
/*
* ----------------------------------------------------------------------------
* Function Name: set_multicast_list
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
static void set_multicast_list (struct net_device *dev)
{
unsigned long flags;
struct ax_device *ax_local = (struct ax_device*)dev->priv;
spin_lock_irqsave (&ax_local->page_lock, flags);
do_set_multicast_list (dev);
spin_unlock_irqrestore (&ax_local->page_lock, flags);
}
/*
* ----------------------------------------------------------------------------
* Function Name: ethdev_init
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
static int ethdev_init (struct net_device *dev)
{
if (dev->priv == NULL)
{
struct ax_device *ax_local;
dev->priv = kmalloc (sizeof (struct ax_device), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
memset (dev->priv, 0, sizeof (struct ax_device));
ax_local = (struct ax_device *)dev->priv;
spin_lock_init (&ax_local->page_lock);
}
dev->hard_start_xmit = &ax_start_xmit;
dev->get_stats = get_stats;
dev->set_multicast_list = &set_multicast_list;
ether_setup (dev);
return 0;
}
/*
* ----------------------------------------------------------------------------
* Function Name: ax88796_PHY_init
* Purpose:
* Params:
* Returns:
* Note:
* ----------------------------------------------------------------------------
*/
void ax88796_PHY_init (struct net_device *dev)
{
struct ax_device *ax_local = (struct ax_device *) dev->priv;
void *ax_base = ax_local->membase;
u16 advertise;
/* Enable AX88796B FOLW CONTROL */
writeb (ENFLOW_ENABLE, ax_base+EN0_FLOW);
advertise = mdio_read (dev, 0x10, MII_ADVERTISE) & ~ADVERTISE_ALL;
switch (ax_local->media) {
case MEDIA_AUTO:
PRINTK (DRIVER_MSG, PFX " The media mode is autosense.\n");
advertise |= ADVERTISE_ALL | 0x400;
break;
case MEDIA_100FULL:
PRINTK (DRIVER_MSG, PFX " The media mode is forced to 100full.\n");
advertise |= ADVERTISE_100FULL | 0x400;
break;
case MEDIA_100HALF:
PRINTK (DRIVER_MSG, PFX " The media mode is forced to 100half.\n");
advertise |= ADVERTISE_100HALF;
break;
case MEDIA_10FULL:
PRINTK (DRIVER_MSG, PFX " The media mode is forced to 10full.\n");
advertise |= ADVERTISE_10FULL;
break;
case MEDIA_10HALF:
PRINTK (DRIVER_MSG, PFX " The media mode is forced to 10half.\n");
advertise |= ADVERTISE_10HALF;
break;
default:
advertise |= ADVERTISE_ALL | 0x400;
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -