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

📄 smc9194.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 4 页
字号:
	/* First two words are status and packet_length */	status 		= inw( ioaddr + DATA_1 );	packet_length 	= inw( ioaddr + DATA_1 );	packet_length &= 0x07ff;  /* mask off top bits */	PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ));	/*	 . the packet length contains 3 extra words :	 . status, length, and an extra word with an odd byte .	*/	packet_length -= 6;	if ( !(status & RS_ERRORS ) ){		/* do stuff to make a new packet */		struct sk_buff  * skb;		byte		* data;		/* read one extra byte */		if ( status & RS_ODDFRAME )			packet_length++;		/* set multicast stats */		if ( status & RS_MULTICAST )			lp->stats.multicast++;		skb = dev_alloc_skb( packet_length + 5);		if ( skb == NULL ) {			printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n");			lp->stats.rx_dropped++;			goto done;		}		/*		 ! This should work without alignment, but it could be		 ! in the worse case		*/		skb_reserve( skb, 2 );   /* 16 bit alignment */		skb->dev = dev;		data = skb_put( skb, packet_length);#ifdef USE_32_BIT		/* 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  */		PRINTK3((" Reading %d dwords (and %d bytes) \n",			packet_length >> 2, packet_length & 3 ));		insl(ioaddr + DATA_1 , data, packet_length >> 2 );		/* read the left over bytes */		insb( ioaddr + DATA_1, 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_1 , data, packet_length >> 1);		if ( packet_length & 1 ) {			data += packet_length & ~1;			*(data++) = inb( ioaddr + DATA_1 );		}#endif#if	SMC_DEBUG > 2			print_packet( data, packet_length );#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 ... */		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:	/*  error or good, tell the card to get rid of this packet */	outw( MC_RELEASE, ioaddr + MMU_CMD );}/************************************************************************* . 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 = netdev_priv(dev);	byte saved_packet;	byte packet_no;	word tx_status;	/* assume bank 2  */	saved_packet = inb( ioaddr + PNR_ARR );	packet_no = inw( ioaddr + FIFO_PORTS );	packet_no &= 0x7F;	/* select this as the packet to read from */	outb( packet_no, ioaddr + PNR_ARR );	/* read the first word from this packet */	outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER );	tx_status = inw( ioaddr + DATA_1 );	PRINTK3((CARDNAME": TX DONE STATUS: %4x \n", tx_status ));	lp->stats.tx_errors++;	if ( tx_status & TS_LOSTCAR ) lp->stats.tx_carrier_errors++;	if ( tx_status & TS_LATCOL  ) {		printk(KERN_DEBUG CARDNAME			": Late collision occurred on last xmit.\n");		lp->stats.tx_window_errors++;	}#if 0		if ( tx_status & TS_16COL ) { ... }#endif	if ( tx_status & TS_SUCCESS ) {		printk(CARDNAME": Successful packet caused interrupt \n");	}	/* re-enable transmit */	SMC_SELECT_BANK( 0 );	outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR );	/* kill the packet */	SMC_SELECT_BANK( 2 );	outw( MC_FREEPKT, ioaddr + MMU_CMD );	/* one less packet waiting for me */	lp->packets_waiting--;	outb( saved_packet, ioaddr + PNR_ARR );	return;}/*-------------------------------------------------------------------- . . This is the main routine of the driver, to handle the 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 irqreturn_t 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 = netdev_priv(dev);	byte	status;	word	card_stats;	byte	mask;	int	timeout;	/* state registers */	word	saved_bank;	word	saved_pointer;#ifdef H4_HACK{  // lets see if it even works:.  // check status and see if its for me.  if((__raw_readl(GPIO1_INT_STAT1)&H4_LAN_GPIO) != H4_LAN_GPIO)    printk("isr, but not for lan chip\n");  // mask gpio in question.  __raw_writel(H4_LAN_GPIO, GPIO1_CLEAR_ISR_EN);  // ack gpio pic to clear source.  __raw_writel(H4_LAN_GPIO, GPIO1_INT_STAT1);}#endif	PRINTK3((CARDNAME": SMC interrupt started \n"));	saved_bank = smc_inw(ioaddr, BANK_SELECT);	SMC_SELECT_BANK(2);	saved_pointer = smc_inw(ioaddr, POINTER);	mask = smc_inb(ioaddr, INT_MASK);	/* clear all interrupts */	outb( 0, ioaddr + INT_MASK );	/* set a timeout value, so I don't stay here forever */	timeout = 4;	PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x \n", mask ));	do {		/* read the status flag, and mask it */		status = smc_inb(ioaddr, INTERRUPT) & mask;		if (!status)			break;		PRINTK3((KERN_WARNING CARDNAME			": Handling interrupt status %x \n", status ));		if (status & IM_RCV_INT) {			/* Got a packet(s). */			PRINTK2((KERN_WARNING CARDNAME				": Receive Interrupt\n"));			smc_rcv(dev);		} else if (status & IM_TX_INT ) {			PRINTK2((KERN_WARNING CARDNAME				": TX ERROR handled\n"));			smc_tx(dev);			outb(IM_TX_INT, ioaddr + INTERRUPT );		} else if (status & IM_TX_EMPTY_INT ) {			/* update stats */			SMC_SELECT_BANK(0);			card_stats = smc_inw(ioaddr, COUNTER);			/* 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 */			SMC_SELECT_BANK(2);			PRINTK2((KERN_WARNING CARDNAME				": TX_BUFFER_EMPTY handled\n"));			outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT );			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 CARDNAME				": Allocation interrupt \n"));			/* 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);			PRINTK2((CARDNAME": Handoff done successfully.\n"));		} else if (status & IM_RX_OVRN_INT ) {			lp->stats.rx_errors++;			lp->stats.rx_fifo_errors++;			outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT );		} else if (status & IM_EPH_INT ) {			PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT \n"));		} else if (status & IM_ERCV_INT ) {			PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT \n"));			outb( IM_ERCV_INT, ioaddr + INTERRUPT );		}	} while (timeout --);	/* restore state register */	SMC_SELECT_BANK(2);	outb( mask, ioaddr + INT_MASK );	PRINTK3(( KERN_WARNING CARDNAME ": MASK is now %x \n", mask ));	outw( saved_pointer, ioaddr + POINTER );	SMC_SELECT_BANK(saved_bank);#ifdef H4_HACK{  // lets see if it even works:.  // unmask interrupt for next one.  __raw_writel(H4_LAN_GPIO, GPIO1_SET_ISR_EN);}#endif	PRINTK3((CARDNAME ": Interrupt done\n"));	return IRQ_HANDLED;}/*---------------------------------------------------- . smc_close . . this makes the board clean up everything that it can . and not talk to the outside world.   Caused by . an 'ifconfig ethX down' . -----------------------------------------------------*/static int smc_close(struct net_device *dev){	netif_stop_queue(dev);	/* clear everything */	smc_shutdown( dev->base_addr );	/* Update the statistics here. */	return 0;}/*------------------------------------------------------------ . Get the current statistics. . This may be called with the card open or closed. .-------------------------------------------------------------*/static struct net_device_stats* smc_query_statistics(struct net_device *dev) {	struct smc_local *lp = netdev_priv(dev);	return &lp->stats;}/*----------------------------------------------------------- . smc_set_multicast_list . . This routine will, depending on the values passed to it, . either make it accept multicast packets, go into . promiscuous mode (for TCPDUMP and cousins) or accept . a select set of multicast packets*/static void smc_set_multicast_list(struct net_device *dev){	short ioaddr = dev->base_addr;	SMC_SELECT_BANK(0);	if (dev->flags & IFF_PROMISC)		smc_outw(smc_inw(ioaddr, RCR) | RCR_PROMISC, ioaddr, RCR);/* BUG?  I never disable promiscuous mode if multicasting was turned on.   Now, I turn off promiscuous mode, but I don't do anything to multicasting   when promiscuous mode is turned on.*/	/* Here, I am setting this to accept all multicast packets.	   I don't need to zero the multicast table, because the flag is	   checked before the table is	*/	else if (dev->flags & IFF_ALLMULTI)		smc_outw(smc_inw(ioaddr, RCR) | RCR_ALMUL, ioaddr, RCR);	/* We just get all multicast packets even if we only want them	 . from one source.  This will be changed at some future	 . point. */	else if (dev->mc_count) {		/* support hardware multicasting */		/* be sure I get rid of flags I might have set */		smc_outw(smc_inw(ioaddr, RCR) & ~(RCR_PROMISC | RCR_ALMUL),			ioaddr, RCR);		/* NOTE: this has to set the bank, so make sure it is the		   last thing called.  The bank is set to zero at the top */		smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list );	}	else {		smc_outw(smc_inw(ioaddr, RCR) & ~(RCR_PROMISC | RCR_ALMUL),			ioaddr, RCR);		/*		  since I'm disabling all multicast entirely, I need to		  clear the multicast list		*/		SMC_SELECT_BANK(3);		smc_outw(0, ioaddr, MULTICAST1);		smc_outw(0, ioaddr, MULTICAST2);		smc_outw(0, ioaddr, MULTICAST3);		smc_outw(0, ioaddr, MULTICAST4);	}}#ifdef MODULEstatic struct net_device *devSMC9194;MODULE_LICENSE("GPL");MODULE_PARM(io, "i");MODULE_PARM(irq, "i");MODULE_PARM(ifport, "i");MODULE_PARM_DESC(io, "SMC 99194 I/O base address");MODULE_PARM_DESC(irq, "SMC 99194 IRQ number");MODULE_PARM_DESC(ifport, "SMC 99194 interface port (0-default, 1-TP, 2-AUI)");int init_module(void){	if (io == 0)		printk(KERN_WARNING		CARDNAME": You shouldn't use auto-probing with insmod!\n" );	/* copy the parameters from insmod into the device structure */	devSMC9194 = smc_init(-1);	if (IS_ERR(devSMC9194))		return PTR_ERR(devSMC9194);	return 0;}void cleanup_module(void){	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */	unregister_netdev(devSMC9194);	free_irq(devSMC9194->irq, devSMC9194);	release_region(devSMC9194->base_addr, SMC_IO_EXTENT);	free_netdev(devSMC9194);}#endif /* MODULE */

⌨️ 快捷键说明

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