Subject: help with tulip port from netbsd to darwin
To: None <tech-net@netbsd.org>
From: chuck remes <cremes@mac.com>
List: tech-net
Date: 04/16/2002 18:44:04
If this is an inappropriate question for the list, my apologies!

I've undertaken the task of porting the tulip driver from freebsd/netbsd 
to darwin. I don't know if you are in the business of helping people port 
this driver to other OSes, but I'm hoping for some help with a few 
questions.  I searched all the archives of tech-net, but virtually none of 
the discussion covers coding of the driver. As an aside, I don't know if 
any of you are familiar with darwin or not, but its driver model is based 
on IOKit which is substantially different from any other bsd unix.  On the 
off chance you'll answer, I'll include as much info as I can.  :-)

Anyway, I'm almost done.  I have receiving working, but transmitting won't 
work no matter what incantations I attempt.  My target chipset is the 
ADMtek 985 (out of a Linksys EtherFast release 5.1, a 21143 clone).  My 
descriptors (chained) are aligned along a quadword boundary (3 lowest bits 
are 0) and I coalesce my outgoing mbuf into a single physical segment.  
Also, to verify bus timing issues aren't throwing me off I have set the 
transmitter for store-and-forward mode.

After tx'ing the first packet, my registers look like:
control   = 0xfef98082 tx_demand = 0xffffffff
rx_demand = 0xffffffff rx_desc   = 0x0ce54808
tx_desc   = 0x0ce54888 status    = 0xfc67c814
netconfig = 0xffb72153 intrrupts = 0xfffe5410
lpc       = 0xfffe0000 MIIeeprom = 0xfff0b7f8
rom       = 0x00000000 timer     = 0xfffe0000
10btstat  = 0x00000000 siareset  = 0x00000200

Here's my empty tx descriptor chain (8 desc).
s = 0x00000000 c = 0x00000000 b1 = 0x00000000 b2 = 0x0ce54898
s = 0x00000000 c = 0x00000000 b1 = 0x00000000 b2 = 0x0ce548a8
s = 0x00000000 c = 0x00000000 b1 = 0x00000000 b2 = 0x0ce548b8
s = 0x00000000 c = 0x00000000 b1 = 0x00000000 b2 = 0x0ce548c8
s = 0x00000000 c = 0x00000000 b1 = 0x00000000 b2 = 0x0ce548d8
s = 0x00000000 c = 0x00000000 b1 = 0x00000000 b2 = 0x0ce548e8
s = 0x00000000 c = 0x00000000 b1 = 0x00000000 b2 = 0x0ce548f8
s = 0x00000000 c = 0x00000000 b1 = 0x00000000 b2 = 0x0ce54888 <- points 
back to first

Here are the values in my descriptor before kicking the transmitter (csr1)
outputPacket, output pkt length = 42
outputPacket, tx mbuf 0x02d5e8d6 in desc 0
outputPacket, B4 s = 0x80000000 c = 0xe100002a b1 = 0x02d5e8d6 b2 = 
0x0ce54898
outputPacket, kick tx-er
outputPacket, AF s = 0x1a078c80 c = 0xe100002a b1 = 0x02d5e8d6 b2 = 
0x0ce54898

The status word gets set to a very bizarre value.  Most of those bits aren'
t even defined (in either the 21143 datasheet or the ADMtek datasheet).  
Those that are say (bit 7) hearbeat failure, (bit 10) no carrier, and (bit 
11) loss of carrier.

After sending a second packet, the status register is 0xfc67c815. The 
lowest bit got set after the second packet!

This is telling me that (bit 2) tx buf unavailable and (bits 22,21) tx 
suspended due to underflow or unavail descriptor.  Strangely, bit 0 is set 
which tells me the tx finished, so at least I know it is reading the 
tx_interrupt bit out of the control word (bit 0 does not set if I don't 
set that bit in the control word of the descriptor).

Darwin runs on PPC, so there are endian differences.  I take care of all 
that by reordering the bytes in the tulip descriptor structure and writing 
the addresses in little-endian to the card (in a method _descOrder()) much 
like the netbsd driver uses the DESC_BO macro.

Here is a segment of code that shows my output packet routine.

UInt32 com_chuck_iokit_tulip::outputPacket(struct mbuf * m, void * param)
{
     if ( !enabledNetif)
     {
         IOLog("%s, interface not enabled\n", __FUNCTION__);
         // drop the packet
         freePacket( m);
         return kIOReturnOutputDropped;
     }

     if ( txActiveCount >= TULIP_TX_RING_LENGTH) {
         IOLog("%s, kIOReturnOutputStall", __FUNCTION__);
         return kIOReturnOutputStall;
     }

     struct IOPhysicalSegment 	vector;
     int				segments;

     // coalesce to one mbuf segment
     segments = txMbufCursor->getPhysicalSegmentsWithCoalesce( m, &vector);

     if ( ! segments)
     {
         // record error stats
         IOLog("%s, getPhysicalSegmentswithCoalesce returned zero\n", 
__FUNCTION__);
         freePacket( m);
         return kIOReturnOutputDropped;
     }

     txActiveCount++; // update counter

     int				i = txHead;
     tulip_descriptor_t *	d = &tx_desc_ring[ txHead];

     IOLog("%s, output pkt length = %d\n", __FUNCTION__, m->m_len);
	// tells chip to use chain-mode, plus length of mbuf seg
     d->control = _descOrder( ADMTEK_TX_CTL_TLINK | m->m_len);
     d->status  = 0;
	// assign physical address to buffer1
     d->buffer1 = _descOrder( vector.location);
	// save the virtual address so we can safely call freePacket()
     tx_mbuf_ring[ i] = m;
     IOLog("%s, tx mbuf 0x%08x in desc %d\n", __FUNCTION__, (UInt32) vector.
location, i);

     d->control |= _descOrder( ADMTEK_TX_CTL_FIRSTFRAG);
     d->control |= _descOrder( ADMTEK_TX_CTL_LASTFRAG);
	// force interrupt every 4 packets to allow for housekeeping tasks
     d->control |= _descOrder( ADMTEK_TX_CTL_FINT);
	// set OWN bit so chip knows it can take control of descriptor
     d->status = _descOrder( ADMTEK_TX_STAT_OWN);

     RING_INCREMENT( txHead, TULIP_TX_RING_LENGTH);
	IOLog("%s, B4 s = 0x%08x c = 0x%08x b1 = 0x%08x b2 = 0x%08x\n", 
__FUNCTION__,
         _descOrder(tx_desc_ring[i].status), 
_descOrder(tx_desc_ring[i].control),
         _descOrder(tx_desc_ring[i].buffer1), 
_descOrder(tx_desc_ring[i].buffer2));

	// kick transmitter
     _writeRegister( ADMTEK_TXSTART, 0xbaadf00d);

     IOLog("%s, kick tx-er\n", __FUNCTION__);IOSleep(20);
	IOLog("%s, AF s = 0x%08x c = 0x%08x b1 = 0x%08x b2 = 0x%08x\n", 
__FUNCTION__,
         _descOrder(tx_desc_ring[i].status), 
_descOrder(tx_desc_ring[i].control),
         _descOrder(tx_desc_ring[i].buffer1), 
_descOrder(tx_desc_ring[i].buffer2));

     return kIOReturnOutputSuccess;
}

Any ideas?

cr