📄 smc9194.c
字号:
int i; int remainder; int lines; printk("Packet of length %d \n", length ); lines = length / 16; remainder = length % 16; for ( i = 0; i < lines ; i ++ ) { int cur; for ( cur = 0; cur < 8; cur ++ ) { byte a, b; a = *(buf ++ ); b = *(buf ++ ); printk("%02x%02x ", a, b ); } printk("\n"); } for ( i = 0; i < remainder/2 ; i++ ) { byte a, b; a = *(buf ++ ); b = *(buf ++ ); printk("%02x%02x ", a, b ); } printk("\n");#endif}#endif/* * Open and Initialize the board * * Set up everything, reset the card, etc .. * */static int smc_open(struct net_device *dev){ int ioaddr = dev->base_addr; int i; /* used to set hw ethernet address */ /* clear out all the junk that was put here before... */ memset(dev->priv, 0, sizeof(struct smc_local)); /* reset the hardware */ smc_reset( ioaddr ); smc_enable( ioaddr ); /* Select which interface to use */ SMC_SELECT_BANK( 1 ); if ( dev->if_port == 1 ) { outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT, ioaddr + CONFIG ); } else if ( dev->if_port == 2 ) { outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT, ioaddr + CONFIG ); } /* 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 + i ); } 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){ /* 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 CARDNAME": transmit timed out, %s?\n", tx_done(dev) ? "IRQ conflict" : "network cable problem"); /* "kick" the adaptor */ smc_reset( dev->base_addr ); smc_enable( dev->base_addr ); dev->trans_start = jiffies; /* clear anything saved */ ((struct smc_local *)dev->priv)->saved_skb = NULL; netif_wake_queue(dev);}/*------------------------------------------------------------- . . 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){ 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 ) dev->stats.multicast++; skb = dev_alloc_skb( packet_length + 5); if ( skb == NULL ) { printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n"); dev->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 */ 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; dev->stats.rx_packets++; dev->stats.rx_bytes += packet_length; } else { /* error ... */ dev->stats.rx_errors++; if ( status & RS_ALGNERR ) dev->stats.rx_frame_errors++; if ( status & (RS_TOOSHORT | RS_TOOLONG ) ) dev->stats.rx_length_errors++; if ( status & RS_BADCRC) dev->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 )); dev->stats.tx_errors++; if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++; if ( tx_status & TS_LATCOL ) { printk(KERN_DEBUG CARDNAME ": Late collision occurred on last xmit.\n"); dev->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 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; int handled = 0; PRINTK3((CARDNAME": SMC interrupt started \n")); saved_bank = inw( ioaddr + BANK_SELECT ); SMC_SELECT_BANK(2); saved_pointer = inw( ioaddr + POINTER ); mask = 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 = inb( ioaddr + INTERRUPT ) & mask; if (!status ) break; handled = 1; 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 = inw( ioaddr + COUNTER ); /* single collisions */ dev->stats.collisions += card_stats & 0xF; card_stats >>= 4; /* multiple collisions */ dev->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; dev->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 ) { dev->stats.rx_errors++; dev->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 ); PRINTK3((CARDNAME ": Interrupt done\n")); return IRQ_RETVAL(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;}/*----------------------------------------------------------- . 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 ) 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 */ else if (dev->flags & IFF_ALLMULTI) 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. */ 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 ); } 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 struct net_device *devSMC9194;MODULE_LICENSE("GPL");module_param(io, int, 0);module_param(irq, int, 0);module_param(ifport, int, 0);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 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 __exit cleanup_module(void){ 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 + -