/* store this information for the driver.. */ struct smc_local { /* IfIhavetowaituntilmemoryisavailabletosend apacket,Iwillstoretheskbuffhere,untilIgetthe desiredmemory.Then,I'llsenditoutandfreeit.
*/ struct sk_buff * saved_skb;
/* .ThiskeepstrackofhowmanypacketsthatIhave .sentout.WhenanTX_EMPTYinterruptcomes,Iknow .thatallofthesehavebeensent.
*/ int packets_waiting;
};
/* this is called to actually send the packet to the chip */ staticvoid smc_hardware_send_packet( struct net_device * dev );
/* Since I am not sure if I will have enough room in the chip's ram .tostorethepacket,Icallthisroutine,whicheithersendsit .now,orgeneratesaninterruptwhenthecardisreadyforthe
. packet */ static netdev_tx_t smc_wait_to_send_packet( struct sk_buff * skb, struct net_device *dev );
/* this does a soft reset on the device */ staticvoid smc_reset( int ioaddr );
/* Enable Interrupts, Receive, and Transmit */ staticvoid smc_enable( int ioaddr );
/* this puts the device in an inactive state */ staticvoid smc_shutdown( int ioaddr );
/* This routine will find the IRQ of the driver if one is not
. specified in the input to the device. */ staticint smc_findirq( int ioaddr );
/* .Function:smc_reset(intioaddr) .Purpose: .ThissetstheSMC91xxchiptoitsnormalstate,hopefullyfromwhatever .messthatanyotherDOSdriverhasputitin. . .MaybeIshouldresetmoreregisterstodefaultsinhere?SOFTRESETshould .dothatforme. . .Method: .1.sendaSOFTRESET .2.waitforittofinish .3.enableautoreleasemode .4.resetthememorymanagementunit .5.clearallinterrupts .
*/ staticvoid smc_reset( int ioaddr )
{ /* This resets the registers mostly to defaults, but doesn't
affect EEPROM. That seems unnecessary */
SMC_SELECT_BANK( 0 );
outw( RCR_SOFTRESET, ioaddr + RCR );
/* this should pause enough for the chip to be happy */
SMC_DELAY( );
/* Set the transmit and receive configuration registers to
default values */
outw( RCR_CLEAR, ioaddr + RCR );
outw( TCR_CLEAR, ioaddr + TCR );
/* set the control register to automatically releasesuccessfullytransmittedpackets,tomakethebest
use out of our limited memory */
SMC_SELECT_BANK( 1 );
outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL );
/* Note: It doesn't seem that waiting for the MMU busy is needed here, butthisisaplacewherefuturechipsets_COULD_break.Bewary
of issuing another MMU command right after this */
outb( 0, ioaddr + INT_MASK );
}
/* .Function:smc_enable .Purpose:letthechiptalktotheoutsidework .Method: .1.Enablethetransmitter .2.Enablethereceiver .3.Enableinterrupts
*/ staticvoid smc_enable( int ioaddr )
{
SMC_SELECT_BANK( 0 ); /* see the header file for options in TCR/RCR NORMAL*/
outw( TCR_NORMAL, ioaddr + TCR );
outw( RCR_NORMAL, ioaddr + RCR );
netif_stop_queue(dev); /* Well, I want to send the packet.. but I don't know
if I can send it right now... */
if ( lp->saved_skb) { /* THIS SHOULD NEVER HAPPEN. */
dev->stats.tx_aborted_errors++;
printk(CARDNAME": Bad Craziness - sent packet while busy.\n" ); return NETDEV_TX_BUSY;
}
lp->saved_skb = skb;
length = skb->len;
if (length < ETH_ZLEN) { if (skb_padto(skb, ETH_ZLEN)) {
netif_wake_queue(dev); return NETDEV_TX_OK;
}
length = ETH_ZLEN;
}
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 */
netif_wake_queue(dev); return NETDEV_TX_OK;
} /* 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 ); /* .PerformanceHack . .waitashortamountoftime..ifIcansendapacketnow,Isend .itnow.Otherwise,Ienableaninterruptandwaitforonetobe .available. . .Icouldhavehandledthisaslightlydifferentway,bycheckingto .seeifanymemorywasavailableintheFREEMEMORYregister.However, .eitherway,Ineedtogenerateanallocation,andtheallocationworks .nomatterwhat,soIsawnopointincheckingfreememory.
*/
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 NETDEV_TX_OK;
} /* or YES! I can send the packet now.. */
smc_hardware_send_packet(dev);
netif_wake_queue(dev); return NETDEV_TX_OK;
}
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! */
netdev_dbg(dev, CARDNAME": Memory allocation failed.\n");
dev_kfree_skb_any(skb);
lp->saved_skb = NULL;
netif_wake_queue(dev); 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'sfastertosendthelongsfirst,andthen .mopupbysendingthelastword.Itdependsheavily .onalignment,atleastonthe486.Maybeitwouldbe .agoodideatocheckwhichisoptimal?Butthatcouldtake .almostasmuchtimeasissaved?
*/ #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. */
/* .Waituntilpositivethattheinterrupthasbeengenerated
*/ 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, asprobe_irq_offwillreturna0anyway,whichiswhatI wantinthiscase.Plus,thecleanupisneededinboth
cases. */
/*--------------------------------------------------------------- .HereIdotypicalinitializationtasks. . .oInitializethestructureifneeded .oprintoutmyvanitymessageifnotdonesoalready .oprintoutwhattypeofhardwareisdetected .oprintouttheethernetaddress .ofindtheIRQ .osetupmyprivatedata .oconfigurethedevstructurewithmysubroutines .oactuallyGRABtheirq. .oGRABtheregion .-----------------------------------------------------------------
*/ staticint __init smc_probe(struct net_device *dev, int ioaddr)
{ int i, memory, retval; unsignedint bank;
constchar *version_string; constchar *if_string;
/* registers */
word revision_register;
word base_address_register;
word configuration_register;
word memory_info_register;
word memory_cfg_register;
u8 addr[ETH_ALEN];
/* Grab the region so that no one else tries to probe our ioports. */ if (!request_region(ioaddr, SMC_IO_EXTENT, DRV_NAME)) return -EBUSY;
dev->irq = irq;
dev->if_port = ifport;
/* First, see if the high byte is 0x33 */
bank = inw( ioaddr + BANK_SELECT ); if ( (bank & 0xFF00) != 0x3300 ) {
retval = -ENODEV; goto err_out;
} /* The above MIGHT indicate a device, but I need to write to further
test this. */
outw( 0x0, ioaddr + BANK_SELECT );
bank = inw( ioaddr + BANK_SELECT ); if ( (bank & 0xFF00 ) != 0x3300 ) {
retval = -ENODEV; goto err_out;
} /* well, we've already written once, so hopefully another time won't hurt.Thistime,Ineedtoswitchthebankregistertobank1,
so I can access the base address register */
SMC_SELECT_BANK(1);
base_address_register = inw( ioaddr + BASE ); if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) {
printk(CARDNAME ": IOADDR %x doesn't match configuration (%x). " "Probably not a SMC chip\n",
ioaddr, base_address_register >> 3 & 0x3E0 ); /* well, the base address register didn't match. Must not have
been a SMC chip after all. */
retval = -ENODEV; goto err_out;
}
/* check if the revision register is something that I recognize. Thesemightneedtobeaddedtolater,asfuturerevisions
could be added. */
SMC_SELECT_BANK(3);
revision_register = inw( ioaddr + REVISION ); if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) { /* I don't recognize this chip, so... */
printk(CARDNAME ": IO %x: Unrecognized revision register:" " %x, Contact author.\n", ioaddr, revision_register);
retval = -ENODEV; goto err_out;
}
/* at this point I'll assume that the chip is an SMC9xxx. ItmightbeprudenttocheckalistingofMACaddresses
against the hardware address, or do some other tests. */
pr_info_once("%s\n", version);
/* fill in some of the fields */
dev->base_addr = ioaddr;
/* .GettheMACaddress(bank1,regs4-9)
*/
SMC_SELECT_BANK( 1 ); for ( i = 0; i < 6; i += 2 ) {
word address;
/* AccordingtoBecker,Ihavetosetthehardwareaddress atthispoint,becausethe(l)usercansetitwithan ioctl.Easilydone...
*/
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 );
}
staticvoid smc_timeout(struct net_device *dev, unsignedint txqueue)
{ /* If we get here, some higher level has decided we are broken.
There should really be a "kick me" function call instead. */
netdev_warn(dev, 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 );
netif_trans_update(dev); /* prevent tx timeout */ /* clear anything saved */
((struct smc_local *)netdev_priv(dev))->saved_skb = NULL;
netif_wake_queue(dev);
}
/*------------------------------------------------------------- . .smc_rcv-receiveapacketfromthecard . .Thereis(atleast)apacketwaitingtobereadfrom .chip-memory. . .oReadthestatus .oIfanerror,recordit .ootherwise,readinthepacket --------------------------------------------------------------
*/ staticvoid 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 );
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 );
}
/* 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));
/* BUG? I never disable promiscuous mode if multicasting was turned on. Now,Iturnoffpromiscuousmode,butIdon'tdoanythingtomulticasting whenpromiscuousmodeisturnedon.
*/
/* Here, I am setting this to accept all multicast packets. Idon'tneedtozerothemulticasttable,becausetheflagis checkedbeforethetableis
*/ elseif (dev->flags & IFF_ALLMULTI)
outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR );
/* We just get all multicast packets even if we only want them .fromonesource.Thiswillbechangedatsomefuture
. point. */ elseif (!netdev_mc_empty(dev)) { /* 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);
} else {
outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
ioaddr + RCR );
staticint __init smc_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); return PTR_ERR_OR_ZERO(devSMC9194);
}
module_init(smc_init_module);
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.67Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.