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

📄 smc91111.c

📁 smc9111网卡芯片的linux驱动程序
💻 C
📖 第 1 页 / 共 5 页
字号:
	unsigned int ioaddr = dev->base_addr;	struct smc_local *lp = (struct smc_local *) dev->priv;	u8 status;	u8 mask;	int timeout;	/* state registers */	u16 saved_bank;	u16 saved_pointer;	PRINTK3("%s: SMC interrupt started \n", dev->name);	if (dev == NULL) {		printk(KERN_WARNING "%s: irq %d for unknown device.\n",		       dev->name, irq);		return;	}	saved_bank = SMC_inw(ioaddr, BANK_SELECT);	SMC_SELECT_BANK(2);	saved_pointer = SMC_inw(ioaddr, PTR_REG);	/* read the interrupt status register */	mask = SMC_inb(ioaddr, IM_REG);	/* disable all interrupts */	SMC_outb(0, ioaddr, IM_REG);	/* set a timeout value, so I don't stay here forever */	timeout = 16;	PRINTK2(KERN_WARNING "%s: MASK IS %x \n", dev->name, mask);	do {		/* read the status flag, and mask it */		status = SMC_inb(ioaddr, INT_REG) & mask;		if (!status)			break;		PRINTK3(KERN_WARNING "%s: Handling interrupt status %x \n",			dev->name, status);		if (status & IM_RCV_INT) {			/* Got a packet(s). */			PRINTK2(KERN_WARNING				"%s: Receive Interrupt\n", dev->name);			smc_rcv(dev);		} else if (status & IM_TX_INT) {			unsigned short ephstatus;			SMC_SELECT_BANK (0);			ephstatus = SMC_inw (ioaddr , EPH_STATUS_REG);			if( ephstatus & ES_TX_SUC)			{#ifdef USE_AUTO_RELEASE#else				SMC_SELECT_BANK(2);				smc_tx(dev);//				mask &= ~(IM_TX_INT);#endif				SMC_SELECT_BANK (2);				SMC_outb(IM_TX_INT, ioaddr , INT_REG );			}			else			{				PRINTK("%s: ##### TX ERROR handled\n", dev->name);				SMC_SELECT_BANK(2);				smc_tx(dev);				// Acknowledge the interrupt				SMC_outb(IM_TX_INT, ioaddr , INT_REG );				if( netif_queue_stopped (dev))					netif_wake_queue(dev);			}		} else if (status & IM_TX_EMPTY_INT) {#ifdef USE_AUTO_RELEASE			byte    tmpstatus;			SMC_SELECT_BANK( 2 );			// Acknowledge the interrupt			SMC_outb( IM_TX_EMPTY_INT, ioaddr , INT_REG );			tmpstatus = SMC_inb( ioaddr , INT_REG );			if( tmpstatus & IM_TX_INT)			{				printk("transmit error!\n");			}			else			{				smc_update_stats( ioaddr, lp);				mask &= ~(IM_TX_EMPTY_INT | IM_TX_INT);				if( netif_queue_stopped (dev))					netif_wake_queue(dev);			}#endif		} else if (status & IM_ALLOC_INT) {			PRINTK2(KERN_DEBUG "%s: Allocation interrupt \n",				dev->name);			/* clear this interrupt so it doesn't happen again */			mask &= ~IM_ALLOC_INT;			/* enable xmit interrupts based on this */#ifdef USE_AUTO_RELEASE			mask |= (IM_TX_EMPTY_INT | IM_TX_INT);#else			mask |= ( IM_TX_INT );#endif			/* send packet to chip */			smc_hardware_send_packet( dev );			SMC_outb( IM_ALLOC_INT, ioaddr, INT_REG );			smc_check_mmu_allocate_complete( ioaddr);			/* and let the card send more packets to me */			/* FB: only wakeup for 100mpbs, something goes poopy if I			 * try it for 10mbps. Many more rcv overruns.			 */			if( lp->rpc_cur_mode & RPC_SPEED)				netif_wake_queue(dev);			PRINTK2("%s: Handoff done successfully.\n", dev->name);		} else if (status & IM_RX_OVRN_INT) {			lp->stats.rx_errors++;			lp->stats.rx_fifo_errors++;			// Acknowledge the interrupt			SMC_outb(IM_RX_OVRN_INT, ioaddr, INT_REG);			PRINTK("%s: rx overrun or fifo error!\n", dev->name);			//printk("fifo err!\n");		} else if (status & IM_MDINT) {			smc_phy_interrupt(dev);			// Acknowledge the interrupt			SMC_outb(IM_MDINT, ioaddr, INT_REG);		} else if (status & IM_EPH_INT) {			PRINTK("%s: UNSUPPORTED: EPH INTERRUPT \n", dev->name);			SMC_outb(IM_EPH_INT, ioaddr, INT_REG);		} else if (status & IM_ERCV_INT) {			PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n", dev->name);			// Acknowledge the interrupt			SMC_outb(IM_ERCV_INT, ioaddr, INT_REG);		}	} while (timeout--);	/* restore register states */	SMC_SELECT_BANK(2);	SMC_outb(mask, ioaddr, IM_REG);	PRINTK3(KERN_WARNING "%s: MASK is now %x \n", dev->name, mask);	while (SMC_inw(ioaddr, PTR_REG) & PTR_NOTEMPTY)		udelay(2);	SMC_outw(saved_pointer, ioaddr, PTR_REG);	SMC_SELECT_BANK(saved_bank);	PRINTK3("%s: Interrupt done\n", dev->name);	return;}/* since we don't use descriptor mode, and activity tends to be quite* bursty, we don't bother with an irq, but if by chance the irqen* bit in the dma channel gets set, this will handle it.*/static voidsmc_dma_irq(int ch, void *lp, struct pt_regs *regs){#if 0	DCSR(ch) = 0;		// Stop channel and disable ints#endif}/*------------------------------------------------------------- . . smc_rcv -  receive a packet from the card . . There is ( at least ) a packet waiting to be read from . chip-memory. . . o Read the status . o If an error, record it . o otherwise, read in the packet --------------------------------------------------------------*/static voidsmc_rcv(struct net_device *dev){	struct smc_local *lp = (struct smc_local *) dev->priv;	unsigned int ioaddr = dev->base_addr;	int packet_number;	u16 status;	u16 packet_length;	u32 inval;	PRINTK3("%s:smc_rcv\n", dev->name);	/* assume bank 2 */	packet_number = SMC_inw(ioaddr, RXFIFO_REG);	if (packet_number & RXFIFO_REMPTY) {		/* we got called , but nothing was on the FIFO */		PRINTK("%s: WARNING: smc_rcv with nothing on FIFO. \n",		       dev->name);		/* don't need to restore anything */		return;	}		PRINTK("Packet number: %x\n", packet_number);		/*  start reading from the start of the packet */	SMC_outw((PTR_READ | PTR_RCV | PTR_AUTOINC), ioaddr, PTR_REG);	/* First two words are status and packet_length */#ifdef CONFIG_ARCH_S3C2410	inval = SMC_inl(ioaddr, DATA_REG);	status = (u16) inval & 0xffff;	packet_length = (u16) (inval >> 16) & 0x7ff;	/* mask off top bits */#else	status = smc_inw(ioaddr, DATA_REG);	packet_length = smc_inw(ioaddr, DATA_REG) & 0x7ff;#endif	PRINTK("status: %x, raw packet_length: %x\n", status, packet_length);	PRINTK2("RCV: STATUS %4x LENGTH %4x\n", status, packet_length);        if (packet_length <= 6)        {                printk(KERN_ERR"Received Packet Size TOO Small (%d) - forcing reset!\n",                                packet_length);#if 0                /* This indicates a bad state, reset the mmu. */                SMC_outw( MC_RESET, ioaddr, MMU_CMD_REG);                return;#endif        }	/* The packet length contains 3 extra words:	   *  status, length, and an extra word with an odd byte (maybe)	 */	packet_length -= 6;	if (!(status & RS_ERRORS)) {		/* do stuff to make a new packet */		struct sk_buff *skb;		unsigned char *data;		int pad = 0;		/* set multicast stats */		if (status & RS_MULTICAST)			lp->stats.multicast++;		/* =>		   ODD-BYTE ISSUE : The odd byte problem has been fixed in the LAN91C111 Rev B.		   So we check if the Chip Revision, stored in smsc_local->ChipRev, is = 1.		   If so then we increment the packet length only if RS_ODDFRAME is set.		   If the Chip's revision is equal to 0, then we blindly increment the packet length		   by 1, thus always assuming that the packet is odd length, leaving the higher layer		   to decide the actual length.		   -- Pramod		   <= */		if ((9 == lp->ChipID) && (1 == lp->ChipRev)) {			if (status & RS_ODDFRAME)				packet_length++;		} else {			// set odd length for bug in LAN91C111, REV A			// which never sets RS_ODDFRAME			packet_length++;		}		// Allocate enough memory for entire receive frame,		// plus alignment needs, to be safe		skb = dev_alloc_skb(packet_length + 12);	//was 5		if (skb == NULL) {			printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",			       dev->name);			lp->stats.rx_dropped++;			goto done;		}		/*                   Apparently dev_alloc_skb is not guaranteed to return a nicely                   aligned data buffer. Make sure we get the alignment we need:		   BIG FAT WARNING: Some architectures (ARM Xscale for		   example) require specific alignments for DMA operations.		   In the case of the PXA250, 64 bit alignment is required		   for memory targets/sources.  		 */		skb_reserve(skb, (-(u32) skb->data) & 7);	/* 64 bit alignment */		skb->dev = dev;		data = skb_put(skb, packet_length);#if defined(CONFIG_ARCH_S3C2410)		/* hack alert: we shift by 2 bytes to make sure the data after		 * the ip header is 4-byte aligned. Note that we do not change		 * the "data" pointer. It has to be 8-byte aligned to 		 * make the dma engine happy. Thus we have to step 2 bytes		 * back in the ptr register (i.e. we will re-read the packet		 * length, but ignore it).		 */		pad = 2;		skb_reserve(skb, pad); /* offset frame past pad bytes */		while(SMC_inw(ioaddr, PTR_REG) & PTR_NOTEMPTY)		{		}		SMC_outw((PTR_READ | PTR_RCV | PTR_AUTOINC | pad), dev->base_addr, PTR_REG);#endif		SMC_ins(ioaddr, DATA_REG, data, packet_length + pad, dev);// This operation doesn't seem to be necessary for the smc91c111...//              while ( SMC_inw( ioaddr, MMU_CMD_REG ) & MC_BUSY )//			udelay(2); // Wait until not busy//		smc_wait_mmu_release_complete( ioaddr);//		SMC_outw(MC_RELEASE, ioaddr, MMU_CMD_REG);#if SMC_DEBUG > 2		printk("Receiving Packet\n");		// print_packet( data, packet_length );		print_packet(skb->data, skb->len);#endif		skb->protocol = eth_type_trans(skb, dev);		netif_rx(skb);		dev->last_rx = jiffies;		lp->stats.rx_packets++;		lp->stats.rx_bytes += packet_length;	} else {		/* error ... */		PRINTK("Got error on receive.\n");		lp->stats.rx_errors++;		if (status & RS_ALGNERR)			lp->stats.rx_frame_errors++;		if (status & (RS_TOOSHORT | RS_TOOLONG))			lp->stats.rx_length_errors++;		if (status & RS_BADCRC)			lp->stats.rx_crc_errors++;	}done://              while ( SMC_inw( ioaddr, MMU_CMD_REG ) & MC_BUSY )//                      udelay(2); // Wait until not busy	smc_wait_mmu_release_complete( ioaddr);	SMC_outw(MC_RELEASE, ioaddr, MMU_CMD_REG);//	smc_wait_mmu_release_complete( ioaddr);	return;}static void smc_update_stats( unsigned int ioaddr, struct smc_local *lp){	u16    card_stats;	/* update stats */	SMC_SELECT_BANK( 0 );	card_stats = SMC_inw( ioaddr , COUNTER_REG );	/* single collisions */	lp->stats.collisions += card_stats & 0xF;	card_stats >>= 4;	/* multiple collisions */	lp->stats.collisions += card_stats & 0xF;	/* these are for when linux supports these statistics */#if 0	card_stats >>= 4;	/* deferred */	card_stats >>= 4;	/* excess deferred */#endif	lp->stats.tx_packets += lp->packets_waiting;	lp->stats.tx_bytes += lp->tx_bytes;	lp->tx_bytes = 0;	lp->packets_waiting = 0;}/************************************************************************* . smc_tx . . Purpose:  Handle a transmit error message.   This will only be called .   when an error, because of the AUTO_RELEASE mode. . . Algorithm: .	Save pointer and packet no .	Get the packet no from the top of the queue .	check if it's valid ( if not, is this an error??? ) .	read the status word .	record the error .	( resend?  Not really, since we don't want old packets around ) .	Restore saved values ************************************************************************/static voidsmc_tx(struct net_device *dev){	unsigned int ioaddr = dev->base_addr;	struct smc_local *lp = (struct smc_local *) dev->priv;	u8 saved_packet;	u8 packet_no;	u16 tx_status;	PRINTK3("%s:smc_tx\n", dev->name);	/* assume bank 2  */	saved_packet = SMC_inb(ioaddr, PN_REG);	packet_no = SMC_inw(ioaddr, RXFIFO_REG);	packet_no &= 0x7F;	/* If the TX FIFO is empty then nothing to do */	if (packet_no & TXFIFO_TEMPTY)		return;	/* select this as the packet to read from */	SMC_outb(packet_no, ioaddr, PN_REG);	/* read the first word (status word) from this packet */	SMC_outw((PTR_AUTOINC | PTR_READ), ioaddr, PTR_REG);	tx_status = SMC_inw(ioaddr, DATA_REG);	PRINTK3("%s: TX DONE STATUS: %4x \n", dev->name, tx_status);	if ( tx_status & TS_SUCCESS ) {#ifdef USE_AUTO_RELEASE#else		smc_update_stats( ioaddr, lp);		if( netif_queue_stopped (dev))			netif_wake_queue(dev);#endif	}	else	{		lp->stats.tx_errors++;		if (tx_status & TS_LOSTCAR)			lp->stats.tx_carrier_errors++;		if (tx_status & TS_LATCOL) {			printk(KERN_DEBUG					"%s: Late collision occurred on last xmit.\n",					dev->name);			lp->stats.tx_window_errors++;#ifdef CONFIG_SYSCTL			lp->ctl_forcol = 0;	// Reset forced collsion#endif		}	}#if 0	if (tx_status & TS_16COL) {	...}#endif#ifdef USE_AUTO_RELEASE	/* re-enable transmit */	SMC_SELECT_BANK(0);	SMC_outw((SMC_inw(ioaddr, TCR_REG) | TCR_ENABLE), ioaddr, TCR_REG);	/* one less packet waiting for me */	lp->packets_waiting--;#endif	/* kill the packet */	SMC_SELECT_BANK(2);        spin_lock_irq(&lp->mmu_lock);        smc_wait_mmu_release_complete( ioaddr);	SMC_outw(MC_FREEPKT, ioaddr, MMU_CMD_REG);	spin_unlock_i

⌨️ 快捷键说明

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