📄 smc9194.c
字号:
#ifndef SUPPORT_OLD_KERNEL/* . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds ) . Purpose: . This sets the internal hardware table to filter out unwanted multicast . packets before they take up memory. . . The SMC chip uses a hash table where the high 6 bits of the CRC of . address are the offset into the table. If that bit is 1, then the . multicast packet is accepted. Otherwise, it's dropped silently. . . To use the 6 bits as an offset into the table, the high 3 bits are the . number of the 8 bit register, while the low 3 bits are the bit within . that register. . . This routine is based very heavily on the one provided by Peter Cammaert.*/static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * addrs ) { int i; unsigned char multicast_table[ 8 ]; struct dev_mc_list * cur_addr; /* table for flipping the order of 3 bits */ unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* start with a table of all zeros: reject all */ memset( multicast_table, 0, sizeof( multicast_table ) ); cur_addr = addrs; for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next ) { int position; /* do we have a pointer here? */ if ( !cur_addr ) break; /* make sure this is a multicast address - shouldn't this be a given if we have it here ? */ if ( !( *cur_addr->dmi_addr & 1 ) ) continue; /* only use the low order bits */ position = crc32( cur_addr->dmi_addr, 6 ) & 0x3f; /* do some messy swapping to put the bit in the right spot */ multicast_table[invert3[position&7]] |= (1<<invert3[(position>>3)&7]); } /* now, the table can be loaded into the chipset */ SMC_SELECT_BANK( 3 ); for ( i = 0; i < 8 ; i++ ) { outb( multicast_table[i], ioaddr + MULTICAST1 + i ); }}/* Finds the CRC32 of a set of bytes. Again, from Peter Cammaert's code.*/static int crc32( char * s, int length ) { /* indices */ int perByte; int perBit; /* crc polynomial for Ethernet */ const unsigned long poly = 0xedb88320; /* crc value - preinitialized to all 1's */ unsigned long crc_value = 0xffffffff; for ( perByte = 0; perByte < length; perByte ++ ) { unsigned char c; c = *(s++); for ( perBit = 0; perBit < 8; perBit++ ) { crc_value = (crc_value>>1)^ (((crc_value^c)&0x01)?poly:0); c >>= 1; } } return crc_value;}#endif/* . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct device * ) . Purpose: . Attempt to allocate memory for a packet, if chip-memory is not . available, then tell the card to generate an interrupt when it . is available. . . Algorithm: . . o if the saved_skb is not currently null, then drop this packet . on the floor. This should never happen, because of TBUSY. . o if the saved_skb is null, then replace it with the current packet, . o See if I can sending it now. . o (NO): Enable interrupts and let the interrupt handler deal with it. . o (YES):Send it now.*/static int smc_wait_to_send_packet( struct sk_buff * skb, struct device * dev ){ struct smc_local *lp = (struct smc_local *)dev->priv; unsigned short ioaddr = dev->base_addr; word length; unsigned short numPages; word time_out; if ( lp->saved_skb) { /* THIS SHOULD NEVER HAPPEN. */ lp->stats.tx_aborted_errors++; printk(CARDNAME": Bad Craziness - sent packet while busy.\n" ); return 1; } lp->saved_skb = skb; length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; /* . the MMU wants the number of pages to be the number of 256 bytes . 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) */ numPages = length / 256; if (numPages > 7 ) { printk(CARDNAME": Far too big packet error. \n"); /* freeing the packet is a good thing here... but should . any packets of this size get down here? */ dev_kfree_skb (skb); lp->saved_skb = NULL; /* this IS an error, but, i don't want the skb saved */ return 0; } /* either way, a packet is waiting now */ lp->packets_waiting++; /* now, try to allocate the memory */ SMC_SELECT_BANK( 2 ); outw( MC_ALLOC | numPages, ioaddr + MMU_CMD ); /* . Performance Hack . . wait a short amount of time.. if I can send a packet now, I send . it now. Otherwise, I enable an interrupt and wait for one to be . available. . . I could have handled this a slightly different way, by checking to . see if any memory was available in the FREE MEMORY register. However, . either way, I need to generate an allocation, and the allocation works . no matter what, so I saw no point in checking free memory. */ time_out = MEMORY_WAIT_TIME; do { word status; status = inb( ioaddr + INTERRUPT ); if ( status & IM_ALLOC_INT ) { /* acknowledge the interrupt */ outb( IM_ALLOC_INT, ioaddr + INTERRUPT ); break; } } while ( -- time_out ); if ( !time_out ) { /* oh well, wait until the chip finds memory later */ SMC_ENABLE_INT( IM_ALLOC_INT ); PRINTK2((CARDNAME": memory allocation deferred. \n")); /* it's deferred, but I'll handle it later */ return 0; } /* or YES! I can send the packet now.. */ smc_hardware_send_packet(dev); return 0;}/* . Function: smc_hardware_send_packet(struct device * ) . Purpose: . This sends the actual packet to the SMC9xxx chip. . . Algorithm: . First, see if a saved_skb is available. . ( this should NOT be called if there is no 'saved_skb' . Now, find the packet number that the chip allocated . Point the data pointers at it in memory . Set the length word in the chip's memory . Dump the packet to chip memory . Check if a last byte is needed ( odd length packet ) . if so, set the control flag right . Tell the card to send it . Enable the transmit interrupt, so I know if it failed . Free the kernel data if I actually sent it.*/static void smc_hardware_send_packet( struct device * dev ){ struct smc_local *lp = (struct smc_local *)dev->priv; byte packet_no; struct sk_buff * skb = lp->saved_skb; word length; unsigned short ioaddr; byte * buf; ioaddr = dev->base_addr; if ( !skb ) { PRINTK((CARDNAME": In XMIT with no packet to send \n")); return; } length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; buf = skb->data; /* If I get here, I _know_ there is a packet slot waiting for me */ packet_no = inb( ioaddr + PNR_ARR + 1 ); if ( packet_no & 0x80 ) { /* or isn't there? BAD CHIP! */ printk(KERN_DEBUG CARDNAME": Memory allocation failed. \n"); kfree(skb); lp->saved_skb = NULL; dev->tbusy = 0; return; } /* we have a packet address, so tell the card to use it */ outb( packet_no, ioaddr + PNR_ARR ); /* point to the beginning of the packet */ outw( PTR_AUTOINC , ioaddr + POINTER ); PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length ));#if SMC_DEBUG > 2 print_packet( buf, length );#endif /* send the packet length ( +6 for status, length and ctl byte ) and the status word ( set to zeros ) */#ifdef USE_32_BIT outl( (length +6 ) << 16 , ioaddr + DATA_1 );#else outw( 0, ioaddr + DATA_1 ); /* send the packet length ( +6 for status words, length, and ctl*/ outb( (length+6) & 0xFF,ioaddr + DATA_1 ); outb( (length+6) >> 8 , ioaddr + DATA_1 );#endif /* send the actual data . I _think_ it's faster to send the longs first, and then . mop up by sending the last word. It depends heavily . on alignment, at least on the 486. Maybe it would be . a good idea to check which is optimal? But that could take . almost as much time as is saved? */#ifdef USE_32_BIT if ( length & 0x2 ) { outsl(ioaddr + DATA_1, buf, length >> 2 ); outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); } else outsl(ioaddr + DATA_1, buf, length >> 2 );#else outsw(ioaddr + DATA_1 , buf, (length ) >> 1);#endif /* Send the last byte, if there is one. */ if ( (length & 1) == 0 ) { outw( 0, ioaddr + DATA_1 ); } else { outb( buf[length -1 ], ioaddr + DATA_1 ); outb( 0x20, ioaddr + DATA_1); } /* enable the interrupts */ SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) ); /* and let the chipset deal with it */ outw( MC_ENQUEUE , ioaddr + MMU_CMD ); PRINTK2((CARDNAME": Sent packet of length %d \n",length)); lp->saved_skb = NULL; dev_kfree_skb (skb); dev->trans_start = jiffies; /* we can send another packet */ dev->tbusy = 0; return;}/*------------------------------------------------------------------------- | | smc_init( struct device * dev ) | Input parameters: | dev->base_addr == 0, try to find all possible locations | dev->base_addr == 1, return failure code | dev->base_addr == 2, always allocate space, and return success | dev->base_addr == <anything else> this is the address to check | | Output: | 0 --> there is a device | anything else, error | ---------------------------------------------------------------------------*/__initfunc(int smc_init(struct device *dev)){ int i; int base_addr = dev ? dev->base_addr : 0; /* try a specific location */ if (base_addr > 0x1ff) { int error; error = smc_probe(base_addr); if ( 0 == error ) { return smc_initcard( dev, base_addr ); } return error; } else { if ( 0 != base_addr ) { return -ENXIO; } } /* check every ethernet address */ for (i = 0; smc_portlist[i]; i++) { int ioaddr = smc_portlist[i]; /* check if the area is available */ if (check_region( ioaddr , SMC_IO_EXTENT)) continue; /* check this specific address */ if ( smc_probe( ioaddr ) == 0) { return smc_initcard( dev, ioaddr ); } } /* couldn't find anything */ return -ENODEV;}#ifndef NO_AUTOPROBE/*---------------------------------------------------------------------- . smc_findirq . . This routine has a simple purpose -- make the SMC chip generate an . interrupt, so an auto-detect routine can detect it, and find the IRQ, ------------------------------------------------------------------------*/__initfunc(int smc_findirq( int ioaddr )){ int timeout = 20; /* I have to do a STI() here, because this is called from a routine that does an CLI during this process, making it rather difficult to get interrupts for auto detection */ sti(); autoirq_setup( 0 ); /* * What I try to do here is trigger an ALLOC_INT. This is done * by allocating a small chunk of memory, which will give an interrupt * when done. */ SMC_SELECT_BANK(2); /* enable ALLOCation interrupts ONLY */ outb( IM_ALLOC_INT, ioaddr + INT_MASK ); /* . Allocate 512 bytes of memory. Note that the chip was just . reset so all the memory is available */ outw( MC_ALLOC | 1, ioaddr + MMU_CMD ); /* . Wait until positive that the interrupt has been generated */ while ( timeout ) { byte int_status; int_status = inb( ioaddr + INTERRUPT ); if ( int_status & IM_ALLOC_INT ) break; /* got the interrupt */ timeout--; } /* there is really nothing that I can do here if timeout fails, as autoirq_report will return a 0 anyway, which is what I want in this case. Plus, the clean up is needed in both cases. */ /* DELAY HERE! On a fast machine, the status might change before the interrupt is given to the processor. This means that the interrupt was never detected, and autoirq_report fails to report anything. This should fix autoirq_* problems. */ SMC_DELAY(); SMC_DELAY(); /* and disable all interrupts again */ outb( 0, ioaddr + INT_MASK ); /* clear hardware interrupts again, because that's how it was when I was called... */ cli(); /* and return what I found */ return autoirq_report( 0 );}#endif/*---------------------------------------------------------------------- . Function: smc_probe( int ioaddr ) . . Purpose: . Tests to see if a given ioaddr points to an SMC9xxx chip. . Returns a 0 on success . . Algorithm: . (1) see if the high byte of BANK_SELECT is 0x33 . (2) compare the ioaddr with the base register's address . (3) see if I recognize the chip ID in the appropriate register . .--------------------------------------------------------------------- */__initfunc(static int smc_probe( int ioaddr )){ unsigned int bank; word revision_register; word base_address_register; /* First, see if the high byte is 0x33 */ bank = inw( ioaddr + BANK_SELECT ); if ( (bank & 0xFF00) != 0x3300 ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -