?? smc91c92_cs.c
字號:
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 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 { /* error ... */ smc->stats.rx_errors++; if (rx_status & RS_ALGNERR) smc->stats.rx_frame_errors++; if (rx_status & (RS_TOOSHORT | RS_TOOLONG)) smc->stats.rx_length_errors++; if (rx_status & RS_BADCRC) smc->stats.rx_crc_errors++; } /* Let the MMU free the memory of this packet. */ outw(MC_RELEASE, ioaddr + MMU_CMD); return;}/*====================================================================*/static struct net_device_stats *smc_get_stats(struct net_device *dev){ struct smc_private *smc = netdev_priv(dev); /* Nothing to update - the 91c92 is a pretty primative chip. */ return &smc->stats;}/*====================================================================== Calculate values for the hardware multicast filter hash table.======================================================================*/static void fill_multicast_tbl(int count, struct dev_mc_list *addrs, u_char *multicast_table){ struct dev_mc_list *mc_addr; for (mc_addr = addrs; mc_addr && count-- > 0; mc_addr = mc_addr->next) { u_int position = ether_crc(6, mc_addr->dmi_addr);#ifndef final_version /* Verify multicast address. */ if ((mc_addr->dmi_addr[0] & 1) == 0) continue;#endif multicast_table[position >> 29] |= 1 << ((position >> 26) & 7); }}/*====================================================================== Set the receive mode. This routine is used by both the protocol level to notify us of promiscuous/multicast mode changes, and by the open/reset code to initialize the Rx registers. We always set the multicast list and leave the receiver running.======================================================================*/static void set_rx_mode(struct net_device *dev){ kio_addr_t ioaddr = dev->base_addr; struct smc_private *smc = netdev_priv(dev); u_int multicast_table[ 2 ] = { 0, }; unsigned long flags; u_short rx_cfg_setting; if (dev->flags & IFF_PROMISC) { rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti; } else if (dev->flags & IFF_ALLMULTI) rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti; else { if (dev->mc_count) { fill_multicast_tbl(dev->mc_count, dev->mc_list, (u_char *)multicast_table); } rx_cfg_setting = RxStripCRC | RxEnable; } /* Load MC table and Rx setting into the chip without interrupts. */ spin_lock_irqsave(&smc->lock, flags); SMC_SELECT_BANK(3); outl(multicast_table[0], ioaddr + MULTICAST0); outl(multicast_table[1], ioaddr + MULTICAST4); SMC_SELECT_BANK(0); outw(rx_cfg_setting, ioaddr + RCR); SMC_SELECT_BANK(2); spin_unlock_irqrestore(&smc->lock, flags); return;}/*====================================================================== Senses when a card's config changes. Here, it's coax or TP.======================================================================*/static int s9k_config(struct net_device *dev, struct ifmap *map){ struct smc_private *smc = netdev_priv(dev); if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { if (smc->cfg & CFG_MII_SELECT) return -EOPNOTSUPP; else if (map->port > 2) return -EINVAL; dev->if_port = map->port; printk(KERN_INFO "%s: switched to %s port\n", dev->name, if_names[dev->if_port]); smc_reset(dev); } return 0;}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -