📄 smc9194.c
字号:
} 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 = 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 */#if 0 card_stats >>= 4; /* deferred */ card_stats >>= 4; /* excess deferred */#endif 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 */ mark_bh( NET_BH ); 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 ); dev->interrupt = 0; PRINTK3((CARDNAME ": Interrupt done\n")); 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 device *dev){ struct smc_local *lp = (struct smc_local *)dev->priv; int ioaddr = dev->base_addr; int packet_number; word status; word packet_length; /* assume bank 2 */ packet_number = inw( ioaddr + FIFO_PORTS ); if ( packet_number & FP_RXEMPTY ) { /* we got called , but nothing was on the FIFO */ PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO. \n")); /* don't need to restore anything */ return; } /* start reading from the start of the packet */ outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER ); /* 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++;#ifdef SUPPORT_OLD_KERNEL skb = alloc_skb( packet_length + 5, GFP_ATOMIC );#else skb = dev_alloc_skb( packet_length + 5);#endif if ( skb == NULL ) { printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n"); lp->stats.rx_dropped++; } /* ! This should work without alignment, but it could be ! in the worse case */#ifndef SUPPORT_OLD_KERNEL /* TODO: Should I use 32bit alignment here ? */ skb_reserve( skb, 2 ); /* 16 bit alignment */#endif skb->dev = dev;#ifdef SUPPORT_OLD_KERNEL skb->len = packet_length; data = skb->data;#else data = skb_put( skb, packet_length);#endif#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 ); if ( packet_length & 1 ) *(data++) = inb( ioaddr + DATA_1 ); insw(ioaddr + DATA_1 , data, (packet_length + 1 ) >> 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#ifndef SUPPORT_OLD_KERNEL skb->protocol = eth_type_trans(skb, dev );#endif 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++; } /* error or good, tell the card to get rid of this packet */ outw( MC_RELEASE, ioaddr + MMU_CMD ); 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 device * dev ){ int ioaddr = dev->base_addr; struct smc_local *lp = (struct smc_local *)dev->priv; 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;}/*---------------------------------------------------- . 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 device *dev){ dev->tbusy = 1; dev->start = 0; /* clear everything */ smc_shutdown( dev->base_addr ); /* Update the statistics here. */#ifdef MODULE MOD_DEC_USE_COUNT;#endif 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 device *dev) { struct smc_local *lp = (struct smc_local *)dev->priv; 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*/#ifdef SUPPORT_OLD_KERNELstatic void smc_set_multicast_list( struct device * dev, int num_addrs, void * addrs )#elsestatic void smc_set_multicast_list(struct device *dev)#endif{ short ioaddr = dev->base_addr; SMC_SELECT_BANK(0);#ifdef SUPPORT_OLD_KERNEL if ( num_addrs < 0 )#else if ( dev->flags & IFF_PROMISC )#endif outw( 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 */#ifdef SUPPORT_OLD_KERNEL else if ( num_addrs > 20 ) /* arbitrary constant */#else else if (dev->flags & IFF_ALLMULTI)#endif outw( 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. */#ifdef SUPPORT_OLD_KERNEL else if (num_addrs > 0 ) {/* the old kernel support will not have hardware multicast support. It would involve more kludges, and make the multicast setting code even worse. Instead, just use the ALMUL method. This is reasonable, considering that it is seldom used*/ outw( inw( ioaddr + RCR ) & ~RCR_PROMISC, ioaddr + RCR ); outw( inw( ioadddr + RCR ) | RCR_ALMUL, ioadddr + RCR ); }#else else if (dev->mc_count ) { /* support hardware multicasting */ /* be sure I get rid of flags I might have set */ outw( 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 ); }#endif else { outw( 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 ); outw( 0, ioaddr + MULTICAST1 ); outw( 0, ioaddr + MULTICAST2 ); outw( 0, ioaddr + MULTICAST3 ); outw( 0, ioaddr + MULTICAST4 ); }}#ifdef MODULEstatic char devicename[9] = { 0, };static struct device devSMC9194 = { devicename, /* device name is inserted by linux/drivers/net/net_init.c */ 0, 0, 0, 0, 0, 0, /* I/O address, IRQ */ 0, 0, 0, NULL, smc_init };int io = 0;int irq = 0;int ifport = 0;MODULE_PARM(io, "i");MODULE_PARM(irq, "i");MODULE_PARM(ifport, "i");int init_module(void){ int result; 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.base_addr = io; devSMC9194.irq = irq; devSMC9194.if_port = ifport; if ((result = register_netdev(&devSMC9194)) != 0) return result; 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); if (devSMC9194.priv) kfree_s(devSMC9194.priv, sizeof(struct smc_local));}#endif /* MODULE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -