RTL8139Private.cpp   [plain text]


/*
 * Copyright (c) 1998-2003, 2006 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/*
 * Copyright (c) 2001 Realtek Semiconductor Corp.  All rights reserved. 
 *
 * RTL8139Private.cpp
 *
 * HISTORY
 *
 * 09-Jul-01	Owen Wei at Realtek Semiconductor Corp. created for Realtek
 *		RTL8139 family NICs.
 */

#include "RTL8139.h"

	//---------------------------------------------------------------------------
	// Check the transmit status register. Inform the NIC to resend the aborted
	// packet. And change the status of device->send_table, device->ack_index,
	// device->send_count.

void RTL8139::transmitterInterrupt( bool *reclaimed )
{
	UInt32	txStatus;
	UInt8	collisions;
	UInt32	tcr;			// Transmit Control Register 0x40

    while ( fTxSendCount )
    {
        txStatus = csrRead32( RTL_TSD0 + fTxAckIndex * sizeof( UInt32 ) );

        if ( (txStatus & (R_TSD_TABT | R_TSD_TOK | R_TSD_TUN)) == 0 )
            break; /// ??? not owned by host (check OWN bit instead?)

		ELG( fTxAckIndex, txStatus, ' Tx+', "RTL8139::transmitterInterrupt" );
        collisions = (txStatus & R_TSD_NCC) >> 24;

        if ( txStatus & R_TSD_TABT ) // transmit abort
        {
			ALRT( fTxAckIndex, txStatus, 'TxA-', "RTL8139::transmitterInterrupt - transmit abort" );
            BUMP_NET_COUNTER( outputErrors );

            tcr = csrRead32( RTL_TCR );
            tcr |= R_TCR_CLRABT;		// resend packet 
            csrWrite32( RTL_TCR, tcr );

				// Record reason for the transmit abort.

            if ( collisions == 0x0F )	BUMP_ETHER_COUNTER( excessiveCollisions );
            if ( txStatus & R_TSD_OWC )	BUMP_ETHER_COUNTER( lateCollisions );
            if ( txStatus & R_TSD_CRS )	BUMP_ETHER_COUNTER( carrierSenseErrors );
            break;  // wait for retransmission
        }

			// The chip will retry if the FIFO was exhausted during the
			// transmission of a packet. The R_TSD_TUN bit will be set.

        if ( txStatus & R_TSD_TUN )
        {
			ALRT( fTxAckIndex, txStatus, 'TxU-', "RTL8139::transmitterInterrupt - transmit underrun" );
            BUMP_ETHER_TX_COUNTER( underruns );
        }

        if ( collisions )
        {
			ALRT( fTxAckIndex, txStatus, 'TxC-', "RTL8139::transmitterInterrupt - transmit collisions" );
            netStats->collisions += collisions;
            if ( collisions > 1 )
                 BUMP_ETHER_COUNTER( multipleCollisionFrames );
            else BUMP_ETHER_COUNTER( singleCollisionFrames );
        }

        fTxSendCount--;
        fTxBufOwnership[ fTxAckIndex ] = kOwnedByHost;

        if ( ++fTxAckIndex >= kTxBufferCount )
			 fTxAckIndex = 0;

        *reclaimed = true;
    }/* end WHILE */
    
    BUMP_ETHER_TX_COUNTER( interrupts );
	return;
}/* end transmitterInterrupt */

	//---------------------------------------------------------------------------

void RTL8139::receiverInterrupt( bool *queued )
{
	mbuf_t		pkt;
	UInt16		status, count;	// from buffer header
	UInt16		pktLen;
	UInt32		totalBytes = 0;
	volatile UInt8	vu8;


	ELG(	fRxOffset << 16 | csrRead16( RTL_CBA ),
			OSReadLittleInt16( fpRxBuffer, fRxOffset ) << 16 | OSReadLittleInt16( fpRxBuffer, fRxOffset + 2 ),
			'Rx I', "RTL8139::receiverInterrupt" );

    while ( true )
    {
		vu8 = csrRead8( RTL_CM );
		if ( vu8 & R_CM_BUFE )
			break;

        status	= OSReadLittleInt16( fpRxBuffer, fRxOffset );
        count	= OSReadLittleInt16( fpRxBuffer, fRxOffset + sizeof( UInt16 ) );

		if ( status == 0xFFFF || count >= 0xFFF0 )
		{
			ELG( status, count, 'BufE', "RTL8139::receiverInterrupt - Buffer should have been Empty" );
			break;			// Break if packet still coming in from FIFO.
		}

		totalBytes	+= count;				/// ???? do this only for M63
		if ( totalBytes > 0x10000 )			// Do no more than 1 Ring at a time.
		{
			ELG( fRxOffset, totalBytes, 'RxXS', "RTL8139::receiverInterrupt - excess pkt processing" );
			break;
		}

        pktLen	= count - 4;		 		// get Rx'd frame length deducting 4 FCS bytes
		fRxOffset += 2 * sizeof( UInt16 );	// move past status/count header

			// Update receiver error counters:

        if ( status & (R_RSR_FAE | R_RSR_CRC | R_RSR_LONG | R_RSR_RUNT | R_RSR_ISE) )
        {
			ALRT( fRxOffset, status << 16 | count, ' Rx-', "RTL8139::receiverInterrupt - error detected" );
            BUMP_NET_COUNTER( inputErrors );

            if ( status & R_RSR_FAE )	BUMP_ETHER_COUNTER(		alignmentErrors	);
            if ( status & R_RSR_CRC )	BUMP_ETHER_COUNTER(		fcsErrors		);
            if ( status & R_RSR_LONG )	BUMP_ETHER_COUNTER(		frameTooLongs	);
            if ( status & R_RSR_RUNT )	BUMP_ETHER_RX_COUNTER(	frameTooShorts	);
            if ( status & R_RSR_ISE )	BUMP_ETHER_RX_COUNTER(	phyErrors		);

				// Will the assertion of any error bits clear the
				// R_RSR_ROK bit? We make it so.

            status &= ~R_RSR_ROK;
        }/* end IF some kind of Rx error */

        if ( (status & R_RSR_ROK) == 0 )
        {
			ALRT( fRxOffset, status << 16 | count, 'Rok-', "RTL8139::receiverInterrupt - Rx restart" );
				// Restart the receiver when an error is encountered.
            restartReceiver();
            BUMP_ETHER_RX_COUNTER( resets );
            DEBUG_LOG( "%s: restarted receiver\n", getName() );
            break;
        }/* end IF Rx not ok and receiver restarted */

        if ( (pktLen < MINPACK - 4) || (pktLen > MAXPACK - 4) )
        {
				// Invalid Rx'd frame length, skip it:

			ALRT( fRxOffset, status << 16 | count, 'RxL-', "RTL8139::receiverInterrupt - length bad" );
            DEBUG_LOG( "%s: bad packet length (%d)\n", getName(), pktLen );
            BUMP_NET_COUNTER( inputErrors );
            if ( pktLen < MINPACK )
                 BUMP_ETHER_RX_COUNTER( frameTooShorts );
            else BUMP_ETHER_COUNTER(    frameTooLongs  );
        }
        else		/*** Good frame, pass it up:	***/
        {
            pkt = allocatePacket( pktLen );
            if ( !pkt )
            {
				ALRT( 0, pktLen, 'RxP-', "RTL8139::receiverInterrupt - packet not allocated" );
                BUMP_ETHER_RX_COUNTER( resourceErrors );
            }
            else
            {
				ELG( fRxOffset, pktLen, 'RxP+', "RTL8139::receiverInterrupt - goodness" );
                assert ( netif );

				if ( fRxOffset < RX_BUF_SIZE - pktLen )
				{
					bcopy( fpRxBuffer + fRxOffset, mbuf_data( pkt ), pktLen );
				}
				else
				{
					ELG( fRxOffset, pktLen, 'Wrap', "RTL8139::receiverInterrupt - Rx buffer wrapped" );
					bcopy( fpRxBuffer + fRxOffset, mbuf_data( pkt ), RX_BUF_SIZE - fRxOffset );
					bcopy( fpRxBuffer, (UInt8*)(mbuf_data( pkt )) + (RX_BUF_SIZE - fRxOffset), pktLen - (RX_BUF_SIZE - fRxOffset) );
				}

                netif->inputPacket( pkt, pktLen, IONetworkInterface:: kInputOptionQueuePacket );
                BUMP_NET_COUNTER( inputPackets );
                *queued = true;
            }
        }/* end if/ELSE good frame */

			// Advance Rx offset to start of the next packet:
		fRxOffset += (count + 3) & ~3;		// round up to 4-byte boundary
		if ( fBuiltin )
			 csrWrite16(     RTL_CAPR, fRxOffset - 0x10 );	// M63
		else csrWrite16Slow( RTL_CAPR, fRxOffset - 0x10 );	// card
    }/* end WHILE buffer not empty */

	return;
}/* end receiverInterrupt */

	//---------------------------------------------------------------------------

void RTL8139::restartReceiver()
{
	UInt8	cm;

	cm = csrRead8( RTL_CM );
	ELG( 0000, cm, 'ReRx', "RTL8139::restartReceiver" );

	cm &= ~R_CM_RE;					// Clear Rx Enable in the Command register
	csrWrite8( RTL_CM, cm );
	IOSleep( 10 );

		/* any timing specifications on stopping the engine? */

	csrWrite32(		RTL_RBSTART,	fRxBufferPhys );
	if ( fBuiltin )
		 csrWrite16(     RTL_CAPR, 0 );	// M63
	else csrWrite16Slow( RTL_CAPR, 0 );	// card
	fRxOffset = 0;

	cm |= R_CM_RE;
	csrWrite8( RTL_CM, cm );		// Set Rx Enable

	csrWrite32( RTL_RCR, reg_rcr );	// Redo the Rx Configuration Register
	return;
}/* end restartReceiver */


void RTL8139::enableHardwareInterrupts()
{
	ELG( 0, fSpeed100, 'eHWI', "RTL8139::enableHardwareInterrupts" );

	if ( fSpeed100 )
	{		// Use Tx interrupts; poll for Rx frames on the Realtek timer:
		csrWrite32( RTL_TIMER_INT, 32768 );	// set about 1 ms in the timer value
		csrWrite32( RTL_TCTR, 0 );			// start the timer
	///	fIMR = R_ISR_TMOUT | R_ISR_TOK | R_ISR_TER;
	///	fIMR = R_ISR_TMOUT | R_ISR_TOK | R_ISR_TER | R_ISR_RER | R_ISR_RXOVW | R_ISR_FOVW;
		fIMR = R_ISR_TMOUT;		// Enable R_ISR_TOK and R_ISR_TER when Tx packet stall
	}
	else	// 10 Mbps:
	{
		fIMR = R_ISR_ALL;
		csrWrite32( RTL_TIMER_INT, 0 );	// turn off the timer
	}

	csrWrite16( RTL_IMR, fIMR );
    interruptEnabled = true;
	return;
}/* end enableHardwareInterrupts */


void RTL8139::disableHardwareInterrupts()
{
	ELG( 0, 0, 'dHWI', "RTL8139::disableHardwareInterrupts" );
    csrWrite16( RTL_IMR, R_ISR_NONE ); 
    interruptEnabled = false;
	return;
}/* end disableHardwareInterrupts */


bool RTL8139::allocateDescriptorMemory()
{
    IOByteCount		len;
	int				size;

		// Allocate Tx and Rx memory:

	size = (TX_BUF_SIZE * kTxBufferCount) + (RX_BUF_SIZE + PAGE_SIZE);

	ELG( 0, size, 'AlDM', "RTL8139::allocateDescriptorMemory - size" );

    fpTxRxMD = IOBufferMemoryDescriptor::withOptions( 
								/* options   */	kIOMemoryPhysicallyContiguous,
								/* capacity  */	size,
								/* alignment */	PAGE_SIZE );

    if ( !fpTxRxMD || fpTxRxMD->prepare() != kIOReturnSuccess )
    {
        IOLog("%s: Can't allocate %d contiguous bytes\n", getName(), size ); 
        return false;
    }
    fpTxRxBuffers	= (UInt8*)fpTxRxMD->getBytesNoCopy();
    fTxRxPhysical	= fpTxRxMD->getPhysicalSegment( 0, &len );

	ELG( fTxRxPhysical, fpTxRxBuffers, 'TxRx', "allocateDescriptorMemory - Tx and Rx buffers" );

		// Put the Rx buffer after the 4 Tx buffers:

    fpRxBuffer		= fpTxRxBuffers   + TX_BUF_SIZE * kTxBufferCount;
    fRxBufferPhys	= fTxRxPhysical   + TX_BUF_SIZE * kTxBufferCount;

    return true;
}/* end allocateDescriptorMemory */

	//---------------------------------------------------------------------------

bool RTL8139::initAdapter( IOOptionBits options )
{
	ELG( 0, options, 'i Ad', "RTL8139::initAdapter" );
    DEBUG_LOG( "initAdapter() ===>\n");

///	disableHardwareInterrupts();

    csrWrite8( RTL_CM, R_CM_RST );	// Issue a software reset.

///	disableHardwareInterrupts();
    IOSleep(10);

    if ( csrRead8( RTL_CM ) & R_CM_RST )
    {
			// FIXME: need more robust recovery (retry?)
        IOLog( "%s: chip reset timed out\n", getName() );
        return false;
    }

		// If all that's needed is a chip reset, then we're done.

    if ( options & kResetChip )
		return true;

		// Clear the multicast hash:

    reg_mar0 = reg_mar4 = 0;
    csrWrite32( RTL_MAR0, reg_mar0 );
    csrWrite32( RTL_MAR4, reg_mar4 );

		// Save config1 register (not used).

    fRegConfig1 = csrRead8( RTL_CONFIG1 );	

		// Update the physical address of the Rx buffer:

	csrWrite32( RTL_RBSTART, fRxBufferPhys );
    fRxOffset = 0;

		// Update the physical address of the Tx buffers:

    for ( int i = 0; i < kTxBufferCount; i++ )
    {
        csrWrite32( RTL_TSAD0 + i * sizeof( UInt32 ), fTxRxPhysical + i * TX_BUF_SIZE );
		fTxBufOwnership[i] = kOwnedByHost;
    }
	fTxSendIndex = fTxSendCount = fTxAckIndex = 0;

		// Enable transmitter and receiver. Seems odd to do this before
		// configuring the transmitter and receiver, but this is how it
		// was in the Realtek source.

    csrWrite8( RTL_CM, R_CM_RE | R_CM_TE );

		// TCR - transmit configuration register.

	csrWrite32( RTL_TCR, R_TCR_MXDMA | R_TCR_IFG );		// 64 B DMA, IFG min - illegal

		// RCR - receive configuration register. Save the value to be used
		// later if the receiver is restarted, or multicast/promiscuous mode
		// changes.

    reg_rcr = R_RCR_RBLEN_64K | R_RCR_AB    | R_RCR_APM   | R_RCR_AM
							  | R_RCR_ERTH  | R_RCR_RXFTH | R_RCR_MXDMA;
    csrWrite32( RTL_RCR, reg_rcr );

    DEBUG_LOG( "initAdapter() <===\n");

    return true;
}/* end initAdapter */

	/* interruptOccurred is called by real Tx interrupts	*/
	/* and by invocations from timeoutOccurred for Rx		*/
	/* or Realtek timer interrupt							*/

void RTL8139::interruptOccurred( IOInterruptEventSource *src, int count )
{
	bool				flushInputQ    = false;
	bool				serviceOutputQ = false;
	UInt16				isr;
	IODebuggerLockState lockState;

		// PCI drivers must be prepared to handle spurious interrupts when the
		// interrupt line is shared with other devices. The interruptEnabled
		// flag prevents the driver from touching hardware before it is ready. 

    if ( !interruptEnabled )
	{
		ELG( 0, 0, 'Int-', "RTL8139::interruptOccurred - not ready" );
		return;
	}

    lockState = IODebuggerLock( this );		// Keep KDP out

	isr = csrRead16( RTL_ISR );
	ELG( csrRead32( RTL_MPC ), isr, 'Int+', "RTL8139::interruptOccurred" );

	if ( fSpeed100 )
	{
		csrWrite32( RTL_TCTR, 0 );		// reset the timer.
		csrWrite16( RTL_IMR, fIMR );	// Mask off possible Tx ints. Reduce this PCI transaction???
	}

	if ( isr )
	{
		if ( isr & (R_ISR_FOVW | R_ISR_RXOVW) )	// If either FIFO or Buffer overflow,
		{										
			ELG( 0, isr, 'RxOv', "RTL8139::interruptOccurred - Rx FIFO or Buffer overflow" );
			isr |= R_ISR_FOVW | R_ISR_RXOVW;	// ack both. /// mlj - I don't know why.
		}
		csrWrite16( RTL_ISR, isr );		// Acknowledge pending interrupt sources

		if ( isr & (R_ISR_TOK | R_ISR_TER) )
		{
			BUMP_ETHER_TX_COUNTER( interrupts );
			transmitterInterrupt( &serviceOutputQ );
			
		}
	}

	if ( isr & (R_ISR_ROK | R_ISR_RER | R_ISR_RXOVW | R_ISR_FOVW) )
	{
		receiverInterrupt( &flushInputQ );
    	BUMP_ETHER_RX_COUNTER( interrupts );
	}

#if NOT_YET
	if ( isr & R_ISR_PUN )		// Packet underrun or link change interrupt:
	{
		UInt16	tmp_rtl74 = csrRead16( RTL_74 );
		if ( tmp_rtl74 & SIZE16_BIT11 ) // link change
		{
			if ( tmp_rtl74 & SIZE16_BIT10 )	{} // link ok
			else							{} // link down
		}
	}
#endif
		// If debugger reclaimed the Tx buffer, then we will still expect a
		// hardware interrupt after returning from the debugger, but the Tx
		// interrupt handler will not reclaim any buffer space. If the output
		// queue was previously stalled, then that could spell trouble. To
		// prevent this, service the output queue when this condition exists.

    if ( fTxBufReclaimed )
    {
        serviceOutputQ	= true;
        fTxBufReclaimed	= false;
    }

    IODebuggerUnlock( lockState );

		// Flush all inbound packets and pass them to the network stack.
		// Interrupts are not enabled until the network interface is enabled
		// by BSD, so netif must be valid.

    assert( netif );
    if ( flushInputQ )
	{
		BUMP_ETHER_RX_COUNTER( interrupts );
		netif->flushInputQueue();
	}

		// Call service() without holding the debugger lock to prevent a
		// deadlock when service() calls our outputPacket() function.

    if ( serviceOutputQ )
		fTransmitQueue->service();
	return;
}/* end interruptOccurred */

	//---------------------------------------------------------------------------
	// Note: This function must not block. Otherwise it may expose
	// re-entrancy issues in the BSD networking stack.
	// Note also that this method is usually called on the client's thread not
	// the workloop.

UInt32 RTL8139::outputPacket( mbuf_t pkt, void *param )
{
    mbuf_t              mn;
    long		        pktLen;
	UInt32				tsd;	// Tx Status of Descriptor
    UInt8				*pDest;
    IODebuggerLockState lockState;

    DEBUG_LOG( "outputPacket() ===>\n" );

    lockState = IODebuggerLock( this );

	tsd = csrRead32( RTL_TSD0 + fTxSendIndex * sizeof( UInt32 ) );

    if ( kOwnedByChip == fTxBufOwnership[ fTxSendIndex ] )
    {
		ELG( fTxSendIndex, tsd, 'Stal', "RTL8139::outputPacket - Tx stall" );
			// Stall the output queue until the ack by the interrupt handler.
        DEBUG_LOG( "outputPacket() <===\n" );
        IODebuggerUnlock( lockState );
		csrWrite16( RTL_IMR, fIMR | R_ISR_TOK | R_ISR_TER );	// Mask ON Tx interrupt
        return kIOReturnOutputStall;
    }

	ELG( fTxSendIndex, mbuf_pkthdr_len( pkt ), 'outP', "RTL8139::outputPacket" );

///	if ( tsd & (R_TSD_CRS | R_TSD_TABT | R_TSD_OWC | R_TSD_CDH | R_TSD_TUN) )
	if ( tsd & (R_TSD_CRS | R_TSD_TABT | R_TSD_OWC			   | R_TSD_TUN) )
	{
		BUMP_NET_COUNTER( outputErrors );
		ALRT( fTxSendIndex, tsd, ' Tx-', "RTL8139::outputPacket - Tx Error" );
	}

    pDest = fpTxRxBuffers + TX_BUF_SIZE * fTxSendIndex;

    pktLen = mbuf_pkthdr_len( pkt );

    for ( mn = pkt; mn; mn = mbuf_next( mn ) )
    {
        bcopy( mbuf_data( mn ), pDest, mbuf_len( mn ) );
        pDest += mbuf_len( mn );
    }

		// Software padding of small frames. Hardware doesn't do this.

    if ( pktLen < MINPACK - 4 )				// account for 4 FCS bytes
    {
        memset( pDest, 0x55, MINPACK - 4 - pktLen );
        pktLen = MINPACK - 4;
    }
		// Start the Tx by setting the frame length and clearing ownership:

    csrWrite32( RTL_TSD0 + fTxSendIndex * sizeof( UInt32 ), pktLen | fTSD_ERTXTH );

	fTxSendCount++;
	fTxBufOwnership[ fTxSendIndex ] = kOwnedByChip;

    if ( ++fTxSendIndex >= kTxBufferCount )
		fTxSendIndex = 0;

	IODebuggerUnlock( lockState );

    freePacket( pkt );

    BUMP_NET_COUNTER( outputPackets );

	DEBUG_LOG( "outputPacket() <===\n" );

    return kIOReturnOutputSuccess;
}/* end outputPacket */


	//---------------------------------------------------------------------------
	// Send KDP packets in polled mode.

void RTL8139::sendPacket( void *pkt, UInt32 pktLen )	// KDP
{
	UInt8	*pDest;

    if ( pktLen > MAXPACK - 4 )	// account for 4 FCS bytes
		return;

		// Poll until a transmit buffer becomes available.

	while ( kOwnedByChip == fTxBufOwnership[ fTxSendIndex ] )
        reclaimTransmitBuffer();

    pDest = fpTxRxBuffers + fTxSendIndex * TX_BUF_SIZE;

    bcopy( pkt, pDest, pktLen );	// Copy debugger packet to the Tx buffer

		// Pad small frames:

    if ( pktLen < MINPACK - 4 )		// account for 4 FCS bytes
    {
        memset( pDest + pktLen, 0x55, MINPACK - 4 - pktLen );
        pktLen = MINPACK - 4;
    }
		// Start the Tx by setting the frame length and clearing ownership:

    csrWrite32( RTL_TSD0 + fTxSendIndex * sizeof( UInt32 ), pktLen | fTSD_ERTXTH );

	fTxSendCount++;
	fTxBufOwnership[ fTxSendIndex ] = kOwnedByChip;

    if ( ++fTxSendIndex >= kTxBufferCount )
		fTxSendIndex = 0;

//	kprintf( "RTL8139: sendPacket len %d\n", pktLen );
	return;
}/* end sendPacket KDP */


void RTL8139::reclaimTransmitBuffer()	// KDP only
{
    UInt32  txStatus;
    UInt32  u32;

    fTxBufReclaimed = true;

	while ( fTxSendCount )
    {
        txStatus = csrRead32( RTL_TSD0 + fTxAckIndex * sizeof( UInt32 ) );

        if ( (txStatus & (R_TSD_TABT | R_TSD_TOK | R_TSD_TUN)) == 0 )
            break;  // not owned by host (check OWN bit instead?)

        if ( txStatus & R_TSD_TABT )		// Tx abort
        {
			ALRT( fTxAckIndex, txStatus, 'TxA-', "RTL8139::reclaimTransmitBuffer - abort" );
            u32 = csrRead32( RTL_TCR );
            u32 |= R_TCR_CLRABT;			// resend packet 
            csrWrite32( RTL_TCR, u32 );
            break;							// wait for retransmission
        }

        fTxSendCount--;
        fTxBufOwnership[ fTxAckIndex ] = kOwnedByHost;

        if ( ++fTxAckIndex >= kTxBufferCount )
			fTxAckIndex = 0;
    }/* end WHILE */

	return;
}/* end reclaimTransmitBuffer */


	//---------------------------------------------------------------------------
	// Receive (KDP) packets in polled mode. This is essentially a simplified
	// version of the receiverInterrupt() function.

void RTL8139::receivePacket( void *pkt, UInt32 *pktLen, UInt32 timeout )	// KDP
{
	UInt16			status, count;	// from buffer header
	UInt16			rxLen;

    *pktLen = 0;
    timeout *= 1000;  // convert ms to us

    while ( timeout && *pktLen == 0 )
    {
        if ( (csrRead8( RTL_CM ) & R_CM_BUFE) == R_CM_BUFE )
        {		// Receive buffer empty, wait and retry.
            IODelay( 50 );
            timeout -= 50;
            continue;
        }

        status	= OSReadLittleInt16( fpRxBuffer, fRxOffset );
        count	= OSReadLittleInt16( fpRxBuffer, fRxOffset + sizeof( UInt16 ) );
        rxLen	= count;		// includes 4 bytes of FCS
		fRxOffset += 2 * sizeof( UInt16 );	// move past buffer header

	//	kprintf( "RTL8139::receivePacket status %x len %d\n", status, rxLen );

        if ( status & (R_RSR_FAE | R_RSR_CRC | R_RSR_LONG | R_RSR_RUNT | R_RSR_ISE) )
             status &= ~R_RSR_ROK;

        if ( (status & R_RSR_ROK) == 0 )
        {
            restartReceiver();
            continue;
        }

        if ( rxLen >= MINPACK && rxLen <= MAXPACK ) 
        {
            bcopy( fpRxBuffer + fRxOffset, pkt, rxLen );
            *pktLen = rxLen;
        }

			// Advance the Rx ring buffer to the start of the next packet:

        fRxOffset += IORound( rxLen, 4 );
        csrWrite16( RTL_CAPR, fRxOffset - 0x10 );	// leave a small gap
    }/* end WHILE */
	return;
}/* end receivePacket KDP */

#pragma mark -
#pragma mark еее Put EEPROM contents in IORegistry еее
#pragma mark -


	/*  register 0x50 RTL_9346CR bits: */

#define RTL_9346CR_PROGRAM	0x80

#define RTL_9346CR_EECS		0x08	/* EEPROM Chip Select							*/
#define RTL_9346CR_EESK		0x04	/* EEPROM Shift Clock							*/
#define RTL_9346CR_EEDI		0x02	/* EEPROM Data In  from EEPROM's perspective	*/
#define RTL_9346CR_EEDO		0x01	/* EEPROM Data Out from EEPROM's perspective	*/

	/* These EEPROM commands include the Start bit 0x4 and	*/
	/* are shifted left 6 for the address field:			*/

#define OP_9346_EWDS	0x100	// Erase/Write Disable
#define OP_9346_EWEN	0x130	// Erase/Write ENable
#define OP_9346_WRITE	0x140
#define OP_9346_READ	0x180
#define OP_9346_ERASE	0x1C0

#define kEEPROM_SIZE	0X80


void RTL8139::registerEEPROM()
{
	OSArray			*arrayStrings;
	OSString		*tmpString;
	char			printBuffer[ 100 ];
	UInt16			contents[ kEEPROM_SIZE / sizeof( UInt16 ) ];
	UInt16			i, x, checksum;

	ELG( 0, 0, 'PROM', "RTL8139::registerEEPROM" );
#if USE_ELG
	UInt32	flag = fpELG->evLogFlag;	// Temporarily disable event logging
	fpELG->evLogFlag = 0;				// because bit bashing floods it.
#endif // USE_ELG

	checksum = 0;

	for ( i = 0; i < kEEPROM_SIZE / sizeof( UInt16 ); ++i )	// For each Word
	{
		x = inEEPROM16( i );
		checksum += x;
		contents[ i ] = x;
	}/* end For each Word */

//	if ( checksum != 0 )
	//	IOLog( "RTL8139::fixEnetFlowControl *** Checksum is incorrect. %04x, %04x\n", contents[ 0x32 / 2 ], checksum );

	if ( (contents[ 6 ] & 0x0002) != 0 )	// Check Flow Control bit
		IOLog( "RTL8139::registerEEPROM - Flow Control is disabled\n" );

		/* put the contents in the IORegistry:	*/

	arrayStrings = OSArray::withCapacity( kEEPROM_SIZE / 0x10 );	// 8 rows of data

#ifdef SPRINTF_DEPRECATED
	for ( i = 0 ; i < (kEEPROM_SIZE / sizeof( UInt16 )); i += 8 )	// 16 bytes per row
	{
		sprintf( printBuffer, "%04x %04x %04x %04x   %04x %04x %04x %04x",
			contents[ i+0 ], contents[i+1], contents[i+2], contents[i+3],
			contents[ i+4 ], contents[i+5], contents[i+6], contents[i+7] );
IOLog( "RTL8139::registerEEPROM: %s\n", printBuffer );
		tmpString = OSString::withCString( printBuffer );
		arrayStrings->setObject( tmpString );
		tmpString->release();
	}
#endif // SPRINTF_DEPRECATED

	setProperty( "EEPROM words", arrayStrings );
	arrayStrings->release();

#ifdef CRAP
	contents[ 6 ] &= ~0x0002;					// Turn off the disable Flow Control bit
	contents[ 0x32 / 2 ] = 0;					// init the new checksum
	checksum = 0;

	for ( i = 0; i < kEEPROM_SIZE / 2; ++i )		// recalc the checksum
		checksum += contents[ i ];

	outEEPROM16( 6, contents[ 6 ] );				// do it.
	outEEPROM16( 0x32 / 2, 0x10000 - checksum );	// adjust the checksum
	
	IOLog( "RTL8139::registerEEPROM - Ethernet Flow Control is now enabled.\n" );
#endif // CRAP

#if USE_ELG
	fpELG->evLogFlag = flag;			// Reenable event logging if it was on.
#endif // USE_ELG

	return;
}/* end registerEEPROM */


void RTL8139::write9346CR( UInt8 value )
{
	csrWrite8( RTL_9346CR, value );
	IOSleep( 1 );

	return;
}/* end write9346CR */


#ifdef CRAP
void RTL8139::outEEPROM16( int location, UInt16 value )
{
	int			i;
	UInt16		outData16 = value;

	write9346CR( RTL_9346CR_PROGRAM );

	outCommand( OP_9346_EWEN );								// enable Erase/Write

	write9346CR( RTL_9346CR_PROGRAM | RTL_9346CR_EECS );	// Clk Lo
	write9346CR( RTL_9346CR_PROGRAM );						// CS Lo

	write9346CR( RTL_9346CR_PROGRAM | RTL_9346CR_EECS );	// CS
	outCommand( OP_9346_ERASE | location );					// Erase the location

	write9346CR( RTL_9346CR_PROGRAM | RTL_9346CR_EECS );	// Clk Lo
	write9346CR( RTL_9346CR_PROGRAM );						// CS Lo

	outCommand( OP_9346_WRITE | location );					// start the Write

	for ( i = 16; i > 0; i-- )
	{
		outBit( outData16 );
		outData16 <<= 1;
	}

	write9346CR( RTL_9346CR_PROGRAM | RTL_9346CR_EECS );	// Clk Lo
	write9346CR( RTL_9346CR_PROGRAM );						// CS Lo

	write9346CR( RTL_9346CR_PROGRAM | RTL_9346CR_EECS );	// CS
	write9346CR( RTL_9346CR_PROGRAM );						// CS Lo

	outCommand( OP_9346_EWDS );								// disable Erase/Write

	write9346CR( RTL_9346CR_PROGRAM | RTL_9346CR_EECS );	// Clk Lo
	write9346CR( RTL_9346CR_PROGRAM );						// CS Lo

	return;
}/* end outEEPROM16 */
#endif // CRAP


int RTL8139::inEEPROM16( int location )
{
	int			i;
	UInt16		inData16 = 0;

	write9346CR( RTL_9346CR_PROGRAM | RTL_9346CR_EECS );	// CS Hi

	outCommand( OP_9346_READ | location );					// Issue the command

		/* Get 16 data bits:	*/

	for ( i = 16; i > 0; i-- )
	{
		inData16 <<= 1;
		inData16 |= inBit();
	}

	write9346CR( RTL_9346CR_PROGRAM );						// CS Lo

	return inData16;
}/* end inEEPROM16 */


void RTL8139::outCommand( UInt16 cmd )
{
	int		i;

	write9346CR( RTL_9346CR_PROGRAM | RTL_9346CR_EECS );	// Chip Select, Clk Lo

		// output the 10 bit command: 1 Zero, 1 Start, 2 OP, 6 Address:

	cmd <<= 6;
	for ( i = 10; i > 0; --i )
	{
		outBit( cmd );
		cmd <<= 1;
	}
	write9346CR( RTL_9346CR_PROGRAM | RTL_9346CR_EECS );	// Chip Select, Clk Lo
	return;
}/* end outCommand */


void RTL8139::outBit( UInt16 bit )
{
	UInt8	u8 = RTL_9346CR_PROGRAM | RTL_9346CR_EECS;

	u8 |= (bit & 0x8000) ? RTL_9346CR_EEDI : 0;

	write9346CR( u8 );						// toggle clock Lo
	write9346CR( u8 | RTL_9346CR_EESK );	// toggle clock Hi
	return;
}/* end outBit */


int RTL8139::inBit()
{
	UInt8	u8 = RTL_9346CR_PROGRAM | RTL_9346CR_EECS;

	write9346CR( u8 | RTL_9346CR_EESK );		// clock Hi
	write9346CR( u8 );							// clock Lo

	u8 = csrRead8( RTL_9346CR );

	return (u8 & RTL_9346CR_EEDO) ? 1 : 0;
}/* end inBit */