📄 smc9194.c
字号:
/* 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 + -