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

📄 smc91111.c

📁 嵌入式网卡lan91c的linux源码
💻 C
📖 第 1 页 / 共 5 页
字号:


/*
 * Open and Initialize the board
 *
 * Set up everything, reset the card, etc ..
 *
 */
static int smc_open(struct net_device *dev)
{
	struct smc_local *lp 	= (struct smc_local *)dev->priv;
	int	ioaddr = dev->base_addr;
	int	i;	/* used to set hw ethernet address */

	PRINTK2("%s:smc_open\n", dev->name);

	/* clear out all the junk that was put here before... */
	memset(dev->priv, 0, sizeof(struct smc_local));

	/* => Store the ChipID and ChipRev, to be used in resolving the Odd-Byte issue in Rev > A of LAN91C111; FM */
	SMC_SELECT_BANK(3);
	lp->ChipID = (inw( ioaddr + REV_REG ) >> 4) & 0xF;
	lp->ChipRev = inw( ioaddr + REV_REG )       & 0xF;

	netif_start_queue(dev);
#ifdef MODULE
	MOD_INC_USE_COUNT;
#endif

	// Setup the default Register Modes
	lp->tcr_cur_mode = TCR_DEFAULT;
	lp->rcr_cur_mode = RCR_DEFAULT;
	lp->rpc_cur_mode = RPC_DEFAULT;

	// Set default parameters (files)
	lp->ctl_swfdup = link_mode&(LINK_SPEED_10FD|LINK_SPEED_100FD)? 1:0;
	lp->ctl_ephloop = 0;
	lp->ctl_miiop = 0;
	lp->ctl_autoneg = link_mode&(LINK_AUTO_NEGOTIATE)? 1:0;
	lp->ctl_rfduplx = 1;
	lp->ctl_rspeed =  link_mode&(LINK_SPEED_100HD|LINK_SPEED_100FD)? 100:10;
	lp->ctl_afduplx = 1;
	lp->ctl_aspeed = 100;
	lp->ctl_lnkfail = 1;
	lp->ctl_forcol = 0;
	lp->ctl_filtcar = 0;

	// Reconfigure default Register Modes from the insmod cmd line link_mode
	if (lp->ctl_swfdup)
		lp->tcr_cur_mode |= TCR_SWFDUP;
	else
		lp->tcr_cur_mode &= ~TCR_SWFDUP;

	if (lp->ctl_autoneg)
		lp->rpc_cur_mode |= RPC_ANEG;
	else
		lp->rpc_cur_mode &= ~RPC_ANEG;

	if (lp->ctl_rfduplx)
		lp->rpc_cur_mode |= RPC_DPLX;
	else
		lp->rpc_cur_mode &= ~RPC_DPLX;

	if (lp->ctl_rspeed == 100)
		lp->rpc_cur_mode |= RPC_SPEED;
	else
		lp->rpc_cur_mode &= ~RPC_SPEED;

	/* reset the hardware */

	smc_reset( dev );
	smc_enable( dev );

	/* Configure the PHY */
	smc_phy_configure(dev);

	smc_enable( dev );

	/*
  		According to Becker, I have to set the hardware address
		at this point, because the (l)user can set it with an
		ioctl.  Easily done...
	*/
	SMC_SELECT_BANK( 1 );
	for ( i = 0; i < 6; i += 2 ) {
		word	address;

		address = dev->dev_addr[ i + 1 ] << 8 ;
		address  |= dev->dev_addr[ i ];
		outw( address, ioaddr + ADDR0_REG + i );
	}

#ifdef CONFIG_SYSCTL
	smc_sysctl_register(dev);
#endif /* CONFIG_SYSCTL */ 

	netif_start_queue(dev);
	return 0;
}

/*--------------------------------------------------------
 . Called by the kernel to send a packet out into the void
 . of the net.  This routine is largely based on
 . skeleton.c, from Becker.
 .--------------------------------------------------------
*/
static void smc_timeout (struct net_device *dev)
{
	struct smc_local *lp 	= (struct smc_local *)dev->priv;

	PRINTK3("%s:smc_send_packet\n", dev->name);

	/* If we get here, some higher level has decided we are broken.
	There should really be a "kick me" function call instead. */
	printk(KERN_WARNING "%s: transmit timed out, %s?\n",dev->name, tx_done(dev) ? "IRQ conflict" :"network cable problem");

	/* clear anything saved */
	if( lp->saved_skb ) {
		dev_kfree_skb (lp->saved_skb);
		lp->saved_skb = NULL;
	}

	/* "kick" the adaptor */
	smc_reset( dev );

	/* Reconfigure the PHY */
	smc_phy_configure(dev);

	smc_enable( dev );

	/* Reconfigure the PROMISC and the MULTICAST mode */
	smc_set_multicast_list(dev);

	dev->trans_start = jiffies;
	netif_wake_queue(dev);
}

/*--------------------------------------------------------------------
 .
 . This is the main routine of the driver, to handle the net_device when
 . it needs some attention.
 .
 . So:
 .   first, save state of the chipset
 .   branch off into routines to handle each case, and acknowledge
 .	    each to the interrupt register
 .   and finally restore state.
 .
 ---------------------------------------------------------------------*/
static void smc_interrupt(int irq, void * dev_id,  struct pt_regs * regs)
{
	struct net_device *dev 	= dev_id;
	int ioaddr 		= dev->base_addr;
	struct smc_local *lp 	= (struct smc_local *)dev->priv;

	byte	status;
	word	card_stats;
	byte	mask;
	int	timeout;
	/* state registers */
	word	saved_bank;
	word	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;
	}

/* will Linux let this happen ??  If not, this costs some speed
	if ( dev->interrupt ) {
		printk(KERN_WARNING "%s: interrupt inside interrupt.\n",
			dev->name);
		return;
	}

	dev->interrupt = 1; */

	saved_bank = inw( ioaddr + BANK_SELECT );

	SMC_SELECT_BANK(2);
	saved_pointer = inw( ioaddr + PTR_REG );

	/* read the interrupt status register */
	mask = inb( ioaddr + IM_REG );

	/* disable all interrupts */
	outb( 0, ioaddr + IM_REG );


	/* set a timeout value, so I don't stay here forever */
	timeout = 4;

	PRINTK2(KERN_WARNING "%s: MASK IS %x \n", dev->name, mask);
	do {
		/* read the status flag, and mask it */
		status = 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 ) {
			PRINTK2(KERN_WARNING "%s: TX ERROR handled\n",
				dev->name);
			smc_tx(dev);
			// Acknowledge the interrupt
			outb(IM_TX_INT, ioaddr + INT_REG );
		} else if (status & IM_TX_EMPTY_INT ) {
			/* update stats */
			SMC_SELECT_BANK( 0 );
			card_stats = 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
			SMC_SELECT_BANK( 2 );
			PRINTK2(KERN_WARNING "%s: TX_BUFFER_EMPTY handled\n",
				dev->name);
			// Acknowledge the interrupt
			outb( IM_TX_EMPTY_INT, ioaddr + INT_REG );
			mask &= ~IM_TX_EMPTY_INT;
			lp->stats.tx_packets += lp->packets_waiting;
			lp->packets_waiting = 0;

		} 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;

			smc_hardware_send_packet( dev );

			/* enable xmit interrupts based on this */
			mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );

			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
			outb( IM_RX_OVRN_INT, ioaddr + INT_REG );
		} else if (status & IM_EPH_INT ) {
			PRINTK("%s: UNSUPPORTED: EPH INTERRUPT \n",
				dev->name);
		} else if (status & IM_MDINT ) {
			smc_phy_interrupt(dev);
			// Acknowledge the interrupt
			outb(IM_MDINT, ioaddr + INT_REG );
		} else if (status & IM_ERCV_INT ) {
			PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n",
				dev->name);
			// Acknowledge the interrupt
			outb( IM_ERCV_INT, ioaddr + INT_REG );
		}
	} while ( timeout -- );


	/* restore register states */

	SMC_SELECT_BANK( 2 );

	outb( mask, ioaddr + IM_REG );

	PRINTK3( KERN_WARNING "%s: MASK is now %x \n", dev->name, mask);
	outw( saved_pointer, ioaddr + PTR_REG );

	SMC_SELECT_BANK( saved_bank );

	//dev->interrupt = 0;
	PRINTK3("%s: Interrupt done\n", dev->name);
	return;
}

/*-------------------------------------------------------------
 .
 . 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 void smc_rcv(struct net_device *dev)
{
	struct smc_local *lp = (struct smc_local *)dev->priv;
	int 	ioaddr = dev->base_addr;
	int 	packet_number;
	word	status;
	word	packet_length;

	PRINTK3("%s:smc_rcv\n", dev->name);

	/* assume bank 2 */

	packet_number = 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;
	}

	/*  start reading from the start of the packet */
	outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + PTR_REG );

	/* First two words are status and packet_length */
	status 		= inw( ioaddr + DATA_REG );
	packet_length 	= inw( ioaddr + DATA_REG );

	packet_length &= 0x07ff;  /* mask off top bits */

	PRINTK2("RCV: STATUS %4x LENGTH %4x\n", status, packet_length );

	if ( !(status & RS_ERRORS ) ){
		/* do stuff to make a new packet */
		struct sk_buff  * skb;
		byte		* data;

		/* set multicast stats */
		if ( status & RS_MULTICAST )
			lp->stats.multicast++;

		/* Adjust for having already read the first two words */
		packet_length -= 4;

		// Allocate enough memory for entire receive frame, to be safe
		skb = dev_alloc_skb( packet_length + 2 );

		if ( skb == NULL ) {
			printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",
				dev->name);
			lp->stats.rx_dropped++;
			goto done;
		}

		/*
		 ! This should work without alignment, but it could be
		 ! in the worse case
		*/
		/* TODO: Should I use 32bit alignment here ? */
		skb_reserve( skb, 2 );   /* 16 bit alignment */

		skb->dev = dev;

		/* =>
    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
		<= */

		// set odd length for bug in LAN91C111, REV A
		// which never sets RS_ODDFRAME
		if ((lp->ChipID == 9) && (lp->ChipRev == 0))
			status |= RS_ODDFRAME;

		if (status & RS_ODDFRAME)
			packet_length += 1;
		data = skb_put( skb, packet_length );	

#ifdef USE_32_BIT
		PRINTK3(" Reading %d dwords (and %d bytes) \n",
			packet_length >> 2, packet_length & 3 );
		/* QUESTION:  Like in the TX routine, do I want
		   to send the DWORDs or the bytes first, or some
		   mixture.  A mixture might improve already slow PIO
		   performance  */
		insl(ioaddr + DATA_REG , data, packet_length >> 2 );
		/* read the left over bytes */
		insb( ioaddr + DATA_REG, data + (packet_length & 0xFFFFFC),
			packet_length & 0x3  );
#else
		PRINTK3(" Reading %d words and %d byte(s) \n",
			(packet_length >> 1 ), packet_length & 1 );
		insw(ioaddr + DATA_REG , data, packet_length >> 1);

#endif // USE_32_BIT

#if	SMC_DEBUG > 2
		printk("Receiving Packet\n");
		print_packet( data, packet_length );
#endif

		skb->protocol = eth_type_trans(skb, dev );
		netif_rx(skb);
		lp->stats.rx_packets++;
	} else {
		/* error ... */
		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++;
	}

	while ( inw( ioaddr + MMU_CMD_REG ) & MC_BUSY )
		udelay(1); // Wait until not busy
done:
	/*  error or good, tell the card to get rid of this packet */
	outw( MC_RELEASE, ioaddr + MMU_CMD_REG );


	return;
}


/*************************************************************************
 . 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 void smc_tx( struct net_device * dev )
{
	int	ioaddr = dev->base_addr;
	struct smc_local *lp = (struct smc_local *)dev->priv;
	byte saved_packet;

⌨️ 快捷键说明

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