⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smc91c92_cs.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
    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 + -