📄 smc91c92_cs.c
字号:
link->open++; netif_start_queue(dev); smc->saved_skb = NULL; smc->packets_waiting = 0; smc_reset(dev); init_timer(&smc->media); smc->media.function = &media_check; smc->media.data = (u_long) dev; smc->media.expires = jiffies + HZ; add_timer(&smc->media); return 0;} /* smc_open *//*====================================================================*/static int smc_close(struct net_device *dev){ struct smc_private *smc = netdev_priv(dev); dev_link_t *link = &smc->link; kio_addr_t ioaddr = dev->base_addr; DEBUG(0, "%s: smc_close(), status %4.4x.\n", dev->name, inw(ioaddr + BANK_SELECT)); netif_stop_queue(dev); /* Shut off all interrupts, and turn off the Tx and Rx sections. Don't bother to check for chip present. */ SMC_SELECT_BANK(2); /* Nominally paranoia, but do no assume... */ outw(0, ioaddr + INTERRUPT); SMC_SELECT_BANK(0); mask_bits(0xff00, ioaddr + RCR); mask_bits(0xff00, ioaddr + TCR); /* Put the chip into power-down mode. */ SMC_SELECT_BANK(1); outw(CTL_POWERDOWN, ioaddr + CONTROL ); link->open--; del_timer_sync(&smc->media); return 0;} /* smc_close *//*====================================================================== Transfer a packet to the hardware and trigger the packet send. This may be called at either from either the Tx queue code or the interrupt handler.======================================================================*/static void smc_hardware_send_packet(struct net_device * dev){ struct smc_private *smc = netdev_priv(dev); struct sk_buff *skb = smc->saved_skb; kio_addr_t ioaddr = dev->base_addr; u_char packet_no; if (!skb) { printk(KERN_ERR "%s: In XMIT with no packet to send.\n", dev->name); return; } /* There should be a packet slot waiting. */ packet_no = inw(ioaddr + PNR_ARR) >> 8; if (packet_no & 0x80) { /* If not, there is a hardware problem! Likely an ejected card. */ printk(KERN_WARNING "%s: 91c92 hardware Tx buffer allocation" " failed, status %#2.2x.\n", dev->name, packet_no); dev_kfree_skb_irq(skb); smc->saved_skb = NULL; netif_start_queue(dev); return; } smc->stats.tx_bytes += skb->len; /* The card should use the just-allocated buffer. */ outw(packet_no, ioaddr + PNR_ARR); /* point to the beginning of the packet */ outw(PTR_AUTOINC , ioaddr + POINTER); /* Send the packet length (+6 for status, length and ctl byte) and the status word (set to zeros). */ { u_char *buf = skb->data; u_int length = skb->len; /* The chip will pad to ethernet min. */ DEBUG(2, "%s: Trying to xmit packet of length %d.\n", dev->name, length); /* send the packet length: +6 for status word, length, and ctl */ outw(0, ioaddr + DATA_1); outw(length + 6, ioaddr + DATA_1); outsw(ioaddr + DATA_1, buf, length >> 1); /* The odd last byte, if there is one, goes in the control word. */ outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1); } /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */ outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) | (inw(ioaddr + INTERRUPT) & 0xff00), ioaddr + INTERRUPT); /* The chip does the rest of the work. */ outw(MC_ENQUEUE , ioaddr + MMU_CMD); smc->saved_skb = NULL; dev_kfree_skb_irq(skb); dev->trans_start = jiffies; netif_start_queue(dev); return;}/*====================================================================*/static void smc_tx_timeout(struct net_device *dev){ struct smc_private *smc = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; printk(KERN_NOTICE "%s: SMC91c92 transmit timed out, " "Tx_status %2.2x status %4.4x.\n", dev->name, inw(ioaddr)&0xff, inw(ioaddr + 2)); smc->stats.tx_errors++; smc_reset(dev); dev->trans_start = jiffies; smc->saved_skb = NULL; netif_wake_queue(dev);}static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct smc_private *smc = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; u_short num_pages; short time_out, ir; netif_stop_queue(dev); DEBUG(2, "%s: smc_start_xmit(length = %d) called," " status %4.4x.\n", dev->name, skb->len, inw(ioaddr + 2)); if (smc->saved_skb) { /* THIS SHOULD NEVER HAPPEN. */ smc->stats.tx_aborted_errors++; printk(KERN_DEBUG "%s: Internal error -- sent packet while busy.\n", dev->name); return 1; } smc->saved_skb = skb; num_pages = skb->len >> 8; if (num_pages > 7) { printk(KERN_ERR "%s: Far too big packet error.\n", dev->name); dev_kfree_skb (skb); smc->saved_skb = NULL; smc->stats.tx_dropped++; return 0; /* Do not re-queue this packet. */ } /* A packet is now waiting. */ smc->packets_waiting++; SMC_SELECT_BANK(2); /* Paranoia, we should always be in window 2 */ /* need MC_RESET to keep the memory consistent. errata? */ if (smc->rx_ovrn) { outw(MC_RESET, ioaddr + MMU_CMD); smc->rx_ovrn = 0; } /* Allocate the memory; send the packet now if we win. */ outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD); for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) { ir = inw(ioaddr+INTERRUPT); if (ir & IM_ALLOC_INT) { /* Acknowledge the interrupt, send the packet. */ outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT); smc_hardware_send_packet(dev); /* Send the packet now.. */ return 0; } } /* Otherwise defer until the Tx-space-allocated interrupt. */ DEBUG(2, "%s: memory allocation deferred.\n", dev->name); outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT); return 0;}/*====================================================================== Handle a Tx anomolous event. Entered while in Window 2.======================================================================*/static void smc_tx_err(struct net_device * dev){ struct smc_private *smc = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; int saved_packet = inw(ioaddr + PNR_ARR) & 0xff; int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f; int tx_status; /* select this as the packet to read from */ outw(packet_no, ioaddr + PNR_ARR); /* read the first word from this packet */ outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER); tx_status = inw(ioaddr + DATA_1); smc->stats.tx_errors++; if (tx_status & TS_LOSTCAR) smc->stats.tx_carrier_errors++; if (tx_status & TS_LATCOL) smc->stats.tx_window_errors++; if (tx_status & TS_16COL) { smc->stats.tx_aborted_errors++; smc->tx_err++; } if (tx_status & TS_SUCCESS) { printk(KERN_NOTICE "%s: Successful packet caused error " "interrupt?\n", dev->name); } /* re-enable transmit */ SMC_SELECT_BANK(0); outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR); SMC_SELECT_BANK(2); outw(MC_FREEPKT, ioaddr + MMU_CMD); /* Free the packet memory. */ /* one less packet waiting for me */ smc->packets_waiting--; outw(saved_packet, ioaddr + PNR_ARR); return;}/*====================================================================*/static void smc_eph_irq(struct net_device *dev){ struct smc_private *smc = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; u_short card_stats, ephs; SMC_SELECT_BANK(0); ephs = inw(ioaddr + EPH); DEBUG(2, "%s: Ethernet protocol handler interrupt, status" " %4.4x.\n", dev->name, ephs); /* Could be a counter roll-over warning: update stats. */ card_stats = inw(ioaddr + COUNTER); /* single collisions */ smc->stats.collisions += card_stats & 0xF; card_stats >>= 4; /* multiple collisions */ smc->stats.collisions += card_stats & 0xF;#if 0 /* These are for when linux supports these statistics */ card_stats >>= 4; /* deferred */ card_stats >>= 4; /* excess deferred */#endif /* If we had a transmit error we must re-enable the transmitter. */ outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR); /* Clear a link error interrupt. */ SMC_SELECT_BANK(1); outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL); outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, ioaddr + CONTROL); SMC_SELECT_BANK(2);}/*====================================================================*/static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = dev_id; struct smc_private *smc = netdev_priv(dev); kio_addr_t ioaddr; u_short saved_bank, saved_pointer, mask, status; unsigned int handled = 1; char bogus_cnt = INTR_WORK; /* Work we are willing to do. */ if (!netif_device_present(dev)) return IRQ_NONE; ioaddr = dev->base_addr; DEBUG(3, "%s: SMC91c92 interrupt %d at %#x.\n", dev->name, irq, ioaddr); smc->watchdog = 0; saved_bank = inw(ioaddr + BANK_SELECT); if ((saved_bank & 0xff00) != 0x3300) { /* The device does not exist -- the card could be off-line, or maybe it has been ejected. */ DEBUG(1, "%s: SMC91c92 interrupt %d for non-existent" "/ejected device.\n", dev->name, irq); handled = 0; goto irq_done; } SMC_SELECT_BANK(2); saved_pointer = inw(ioaddr + POINTER); mask = inw(ioaddr + INTERRUPT) >> 8; /* clear all interrupts */ outw(0, ioaddr + INTERRUPT); do { /* read the status flag, and mask it */ status = inw(ioaddr + INTERRUPT) & 0xff; DEBUG(3, "%s: Status is %#2.2x (mask %#2.2x).\n", dev->name, status, mask); if ((status & mask) == 0) { if (bogus_cnt == INTR_WORK) handled = 0; break; } if (status & IM_RCV_INT) { /* Got a packet(s). */ smc_rx(dev); } if (status & IM_TX_INT) { smc_tx_err(dev); outw(IM_TX_INT, ioaddr + INTERRUPT); } status &= mask; if (status & IM_TX_EMPTY_INT) { outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT); mask &= ~IM_TX_EMPTY_INT; smc->stats.tx_packets += smc->packets_waiting; smc->packets_waiting = 0; } if (status & IM_ALLOC_INT) { /* Clear this interrupt so it doesn't happen again */ mask &= ~IM_ALLOC_INT; smc_hardware_send_packet(dev); /* enable xmit interrupts based on this */ mask |= (IM_TX_EMPTY_INT | IM_TX_INT); /* and let the card send more packets to me */ netif_wake_queue(dev); } if (status & IM_RX_OVRN_INT) { smc->stats.rx_errors++; smc->stats.rx_fifo_errors++; if (smc->duplex) smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */ outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT); } if (status & IM_EPH_INT) smc_eph_irq(dev); } while (--bogus_cnt); DEBUG(3, " Restoring saved registers mask %2.2x bank %4.4x" " pointer %4.4x.\n", mask, saved_bank, saved_pointer); /* restore state register */ outw((mask<<8), ioaddr + INTERRUPT); outw(saved_pointer, ioaddr + POINTER); SMC_SELECT_BANK(saved_bank); DEBUG(3, "%s: Exiting interrupt IRQ%d.\n", dev->name, irq);irq_done: if ((smc->manfid == MANFID_OSITECH) && (smc->cardid != PRODID_OSITECH_SEVEN)) { /* Retrigger interrupt if needed */ mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR); set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR); } if (smc->manfid == MANFID_MOTOROLA) { u_char cor; cor = readb(smc->base + MOT_UART + CISREG_COR); writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR); writeb(cor, smc->base + MOT_UART + CISREG_COR); cor = readb(smc->base + MOT_LAN + CISREG_COR); writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR); writeb(cor, smc->base + MOT_LAN + CISREG_COR); }#ifdef DOES_NOT_WORK if (smc->base != NULL) { /* Megahertz MFC's */ readb(smc->base+MEGAHERTZ_ISR); readb(smc->base+MEGAHERTZ_ISR); }#endif return IRQ_RETVAL(handled);}/*====================================================================*/static void smc_rx(struct net_device *dev){ struct smc_private *smc = netdev_priv(dev); kio_addr_t ioaddr = dev->base_addr; int rx_status; int packet_length; /* Caution: not frame length, rather words to transfer from the chip. */ /* Assertion: we are in Window 2. */ if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) { printk(KERN_ERR "%s: smc_rx() with nothing on Rx FIFO.\n", dev->name); return; } /* Reset the read pointer, and read the status and packet length. */ outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER); rx_status = inw(ioaddr + DATA_1); packet_length = inw(ioaddr + DATA_1) & 0x07ff; DEBUG(2, "%s: Receive status %4.4x length %d.\n", dev->name, rx_status, packet_length); if (!(rx_status & RS_ERRORS)) { /* do stuff to make a new packet */ struct sk_buff *skb; /* Note: packet_length adds 5 or 6 extra bytes here! */ skb = dev_alloc_skb(packet_length+2); if (skb == NULL) { DEBUG(1, "%s: Low memory, packet dropped.\n", dev->name); smc->stats.rx_dropped++; outw(MC_RELEASE, ioaddr + MMU_CMD); return; } packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6); skb_reserve(skb, 2); insw(ioaddr+DATA_1, skb_put(skb, packet_length), (packet_length+1)>>1); skb->protocol = eth_type_trans(skb, dev); skb->dev = dev; netif_rx(skb); dev->last_rx = jiffies; smc->stats.rx_packets++; smc->stats.rx_bytes += packet_length; if (rx_status & RS_MULTICAST) smc->stats.multicast++; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -