📄 smc91113.c
字号:
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 ); /* and let the card send more packets to me */ 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 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;}/*Qinwei : fix the problem when using insw(...)*/static void SMC_insw(r,b,l){ int __i ; unsigned short *__b2; __b2 = (unsigned short *) b; for (__i = 0; __i < l; __i++) { *(__b2 + __i) = inw(r); inw(0x2000300); };}/*Qinwei : fix the problem when using outsw(...)*/static void SMC_outsw(r,b,l){ int __i; unsigned short *__b2; __b2 = (unsigned short *) b; for (__i = 0; __i < l; __i++) { outw( *(__b2 + __i), r); } }/*------------------------------------------------------------- . . 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 ); // Qinwei: it seems couldn't get the status??? status = RS_ODDFRAME; // So add this line. 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++; // Allocate enough memory for entire receive frame, to be safe skb = dev_alloc_skb( packet_length ); /* Adjust for having already read the first two words */ packet_length -= 4; 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 LAN91C113 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) data = skb_put( skb, packet_length + 1 ); else data = skb_put( skb, packet_length); } else { // set odd length for bug in LAN91C113, REV A // which never sets RS_ODDFRAME data = skb_put( skb, packet_length + 1 ); }#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); // Qinwei ??? SMC_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 busydone: /* 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; byte packet_no; word tx_status; PRINTK3("%s:smc_tx\n", dev->name); /* assume bank 2 */ saved_packet = inb( ioaddr + PN_REG ); packet_no = 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 */ outb( packet_no, ioaddr + PN_REG ); /* read the first word (status word) from this packet */ outw( PTR_AUTOINC | PTR_READ, ioaddr + PTR_REG ); tx_status = inw( ioaddr + DATA_REG ); PRINTK3("%s: TX DONE STATUS: %4x \n", dev->name, tx_status); 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 //CONFIG_SYSCTL }#if 0 if ( tx_status & TS_16COL ) { ... }#endif if ( tx_status & TS_SUCCESS ) { printk("%s: Successful packet caused interrupt \n", dev->name); } /* re-enable transmit */ SMC_SELECT_BANK( 0 ); outw( inw( ioaddr + TCR_REG ) | TCR_ENABLE, ioaddr + TCR_REG ); /* kill the packet */ SMC_SELECT_BANK( 2 ); outw( MC_FREEPKT, ioaddr + MMU_CMD_REG ); /* one less packet waiting for me */ lp->packets_waiting--; /* Don't change Packet Number Reg until busy bit is cleared */ /* Per LAN91C113 Spec, Page 50 */ while ( inw( ioaddr + MMU_CMD_REG ) & MC_BUSY ); outb( saved_packet, ioaddr + PN_REG ); 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 net_device *dev){ netif_stop_queue(dev); //dev->start = 0; PRINTK2("%s:smc_close\n", dev->name);#ifdef CONFIG_SYSCTL smc_sysctl_unregister(dev);#endif /* CONFIG_SYSCTL */ /* clear everything */ smc_shutdown( dev->base_addr ); /*Qinwei: close the interupt */ disable_irq(dev->irq); 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 = (struct smc_local *)dev->priv; PRINTK2("%s:smc_query_statistics\n", dev->name); 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){ unsigned int ioaddr = dev->base_addr; PRINTK2("%s:smc_set_multicast_list\n", dev->name); SMC_SELECT_BANK(0); if ( dev->flags & IFF_PROMISC ) { PRINTK2("%s:smc_set_multicast_list:RCR_PRMS\n", dev->name); outw( inw(ioaddr + RCR_REG ) | RCR_PRMS, ioaddr + RCR_REG ); }/* 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) { outw( inw(ioaddr + RCR_REG ) | RCR_ALMUL, ioaddr + RCR_REG ); PRINTK2("%s:smc_set_multicast_list:RCR_ALMUL\n", dev->name); } /* 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 */ outw( inw( ioaddr + RCR_REG ) & ~(RCR_PRMS | RCR_ALMUL), ioaddr + RCR_REG ); /* 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 { PRINTK2("%s:smc_set_multicast_list:~(RCR_PRMS|RCR_ALMUL)\n", dev->name); outw( inw( ioaddr + RCR_REG ) & ~(RCR_PRMS | RCR_ALMUL), ioaddr + RCR_REG ); /* since I'm disabling all multicast entirely, I need to clear the multicast list */ SMC_SELECT_BANK( 3 ); outw( 0, ioaddr + MCAST_REG1 ); outw( 0, ioaddr + MCAST_REG2 ); outw( 0, ioaddr + MCAST_REG3 ); outw( 0, ioaddr + MCAST_REG4 ); }}static struct net_device devSMC91113;/*------------------------------------------------------------ . Module initialization function .-------------------------------------------------------------*/int EV44b0_init_module(void){ int result; PRINTK2("CARDNAME:init_module\n"); /* copy the parameters from insmod into the device structure */ devSMC91113.base_addr = CONFIG_SMC91113_BASE; // io base address devSMC91113.irq = CONFIG_SMC91113_IRQ; // irq number devSMC91113.dma = CONFIG_SMC91113_DMA; // Use DMA field for nowait? devSMC91113.init = smc_init;/* Kernel 2.4 Changes - Pramod */ if ((result = register_netdev(&devSMC91113)) != 0) { printk("EV44B0 eth: Error %i registering device \"%s\"\n", result, devSMC91113.name); return -ENODEV;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -