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

📄 smc91c92_cs.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
        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);	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)	    break;		if (status & IM_RCV_INT) {	    /* Got a packet(s). */	    smc_rx(dev);	    smc->last_rx = jiffies;	}	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++;			    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}/*====================================================================*/static void smc_rx(struct net_device *dev){    struct smc_private *smc = (struct smc_private *)dev->priv;    ioaddr_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);	smc->stats.rx_packets++;	smc->stats.rx_bytes += skb->len;	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 *smc91c92_get_stats(struct net_device *dev){    struct smc_private *smc = (struct smc_private *)dev->priv;    /* Nothing to update - the 91c92 is a pretty primative chip. */    return &smc->stats;}/*======================================================================    Compute the AUTODIN polynomial "CRC32" for ethernet packets.======================================================================*/static const u_int ethernet_polynomial = 0x04c11db7U;static u_int ether_crc(int length, u_char *data){    int crc = 0xffffffff;	/* Initial value. */        while (--length >= 0) {	u_char current_octet = *data++;	int bit;	for (bit = 0; bit < 8; bit++, current_octet >>= 1) {	    crc = (crc << 1) ^		((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);	}    }    /* The hash index is the either the upper or lower bits of the CRC, so     * we return the entire CRC.     */    return crc;}/*======================================================================      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){    ioaddr_t ioaddr = dev->base_addr;    u_int multicast_table[ 2 ] = { 0, };    long flags;    u_short rx_cfg_setting;        if (dev->flags & IFF_PROMISC) {	printk(KERN_NOTICE "%s: setting Rx mode to promiscuous.\n", dev->name);	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. */    save_flags(flags);    cli();    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);    restore_flags(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 = dev->priv;    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;}/*======================================================================    Reset the chip, reloading every register that might be corrupted.======================================================================*//*  Set transceiver type, perhaps to something other than what the user  specified in dev->if_port.*/static void smc_set_xcvr(struct net_device *dev, int if_port){    struct smc_private *smc = (struct smc_private *)dev->priv;    ioaddr_t ioaddr = dev->base_addr;    u_short saved_bank;    saved_bank = inw(ioaddr + BANK_SELECT);    SMC_SELECT_BANK(1);    if (if_port == 2) {	outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG);	if ((smc->manfid == MANFID_OSITECH) &&	    (smc->cardid != PRODID_OSITECH_SEVEN))	    set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);	smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002);    } else {	outw(smc->cfg, ioaddr + CONFIG);	if ((smc->manfid == MANFID_OSITECH) &&	    (smc->cardid != PRODID_OSITECH_SEVEN))	    mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);	smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001);    }    SMC_SELECT_BANK(saved_bank);}static void smc_reset(struct net_device *dev){    ioaddr_t ioaddr = dev->base_addr;    struct smc_private *smc = dev->priv;    int i;    DEBUG(0, "%s: smc91c92 reset called.\n", dev->name);        /* The first interaction must be a write to bring the chip out       of sleep mode. */    SMC_SELECT_BANK(0);    /* Reset the chip. */    outw(RCR_SOFTRESET, ioaddr + RCR);    udelay(10);        /* Clear the transmit and receive configuration registers. */    outw(RCR_CLEAR, ioaddr + RCR);    outw(TCR_CLEAR, ioaddr + TCR);        /* Set the Window 1 control, configuration and station addr registers.       No point in writing the I/O base register ;-> */    SMC_SELECT_BANK(1);    /* Automatically release succesfully transmitted packets,       Accept link errors, counter and Tx error interrupts. */    outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,	 ioaddr + CONTROL);    smc_set_xcvr(dev, dev->if_port);    if ((smc->manfid == MANFID_OSITECH) &&	(smc->cardid != PRODID_OSITECH_SEVEN))	outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |	     (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),	     ioaddr - 0x10 + OSITECH_AUI_PWR);        /* Fill in the physical address.  The databook is wrong about the order! */    for (i = 0; i < 6; i += 2)	outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],	     ioaddr + ADDR0 + i);        /* Reset the MMU */    SMC_SELECT_BANK(2);    outw(MC_RESET, ioaddr + MMU_CMD);    outw(0, ioaddr + INTERRUPT);        /* Re-enable the chip. */    SMC_SELECT_BANK(0);    outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |	 TCR_ENABLE | TCR_PAD_EN, ioaddr + TCR);    set_rx_mode(dev);    /* Enable interrupts. */    SMC_SELECT_BANK(2);    outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,	 ioaddr + INTERRUPT);}/*======================================================================    Media selection timer routine    ======================================================================*/static void media_check(u_long arg){    struct smc_private *smc = (struct smc_private *)(arg);    struct net_device *dev = &smc->dev;    ioaddr_t ioaddr = dev->base_addr;    u_short i, media, saved_bank;    if (!netif_device_present(dev))	goto reschedule;    saved_bank = inw(ioaddr + BANK_SELECT);    SMC_SELECT_BANK(2);    i = inw(ioaddr + INTERRUPT);    SMC_SELECT_BANK(0);    media = inw(ioaddr + EPH) & EPH_LINK_OK;    SMC_SELECT_BANK(1);    media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;    SMC_SELECT_BANK(saved_bank);        /* Check for pending interrupt with watchdog flag set: with       this, we can limp along even if the interrupt is blocked */    if (smc->watchdog++ && ((i>>8) & i)) {	if (!smc->fast_poll)	    printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);	smc_interrupt(dev->irq, smc, NULL);	smc->fast_poll = HZ;    }    if (smc->fast_poll) {	smc->fast_poll--;	smc->media.expires = jiffies + 1;	add_timer(&smc->media);	return;    }    if (smc->cfg & CFG_MII_SELECT)	goto reschedule;    /* Ignore collisions unless we've had no rx's recently */    if (jiffies - smc->last_rx > HZ) {	if (smc->tx_err || (smc->media_status & EPH_16COL))	    media |= EPH_16COL;    }    smc->tx_err = 0;    if (media != smc->media_status) {	if ((media & smc->media_status & 1) &&	    ((smc->media_status ^ media) & EPH_LINK_OK))	    printk(KERN_INFO "%s: %s link beat\n", dev->name,		   (smc->media_status & EPH_LINK_OK ? "lost" : "found"));	else if ((media & smc->media_status & 2) &&		 ((smc->media_status ^ media) & EPH_16COL))	    printk(KERN_INFO "%s: coax cable %s\n", dev->name,		   (media & EPH_16COL ? "problem" : "ok"));	if (dev->if_port == 0) {	    if (media & 1) {		if (media & EPH_LINK_OK)		    printk(KERN_INFO "%s: flipped to 10baseT\n",			   dev->name);		else		    smc_set_xcvr(dev, 2);	    } else {		if (media & EPH_16COL)		    smc_set_xcvr(dev, 1);		else		    printk(KERN_INFO "%s: flipped to 10base2\n",			   dev->name);	    }	}	smc->media_status = media;    }    reschedule:    smc->media.expires = jiffies + HZ;    add_timer(&smc->media);}/*====================================================================*/static int __init init_smc91c92_cs(void){    servinfo_t serv;    DEBUG(0, "%s\n", version);    CardServices(GetCardServicesInfo, &serv);    if (serv.Revision != CS_RELEASE_CODE) {	printk(KERN_ERR	       "smc91c92_cs: Card Services release does not match!\n");	return -1;    }    register_pccard_driver(&dev_info, &smc91c92_attach, &smc91c92_detach);    return 0;}static void __exit exit_smc91c92_cs(void){    DEBUG(0, "smc91c92_cs: unloading\n");    unregister_pccard_driver(&dev_info);    while (dev_list != NULL)	smc91c92_detach(dev_list);}module_init(init_smc91c92_cs);module_exit(exit_smc91c92_cs);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -