Пример #1
0
void LinkRxSetup(unsigned char *bssid, unsigned char *macaddr)
{
    DeviceReceiveDisable();
    // 
	// cleanup descriptors created last time
	//
	LinkRxLoopDestroy(0);
    /*
     * create a few descriptors linked in a loop 
     */
    LinkRxLoopCreate(MQUEUE);

#ifdef UNUSED
	this should always be turned on

    ar5kInitData[pLibDev->ar5kInitIndex].pMacAPI->setPPM(0, enablePPM);

    pLibDev->rx.rxEnable = 1;
#endif

	DeviceStationIdSet(macaddr);
	DeviceBssIdSet(bssid);
    DeviceReceiveDeafMode(0);
    return;
}
Пример #2
0
void LinkRxComplete(int timeout, 
	int ndump, unsigned char *dataPattern, int dataPatternLength, int (*done)())
{
    unsigned int startTime, endTime, ctime;
	int it;
	int notdone;
	int stop;
	int behind;
	int scount;
	unsigned int mDescriptor;			// address of descriptor in shared memory
#ifdef MEMORYREAD
	unsigned int descriptor[MDESCRIPTOR+30];	// private copy of current descriptor
#else
    unsigned int *descriptor;
#endif
	int pass;
    int dsize;
	int isdone;

	pass=0;
	dataPatternLength=0;
	dataPattern=0;
	scount=0;
    dsize=RxDescriptorSize();
    //
	// Loop timeout condition.
	// This number can be large since it is only used for
	// catastrophic failure of cart. The normal terminating
	// condition is a message from cart saying STOP.
	//
    startTime=TimeMillisecond();
	ctime=startTime;
//	if(timeout<=0)
//	{
//		timeout=60*60*1000;             // one hour
//	}
	endTime=startTime+timeout;		
	//
	// set pointer to first descriptor
	//
//    mDescriptor = LinkRxLoopFirst(0);
	//
	// keep track of how many times we look and the descriptor is not done
	// this is used to control the sleep interval
	//
	notdone=0;
	stop=0;
	behind=0;
	//
	// loop looking for descriptors with received packets.
	//
	for(it = 0; ; it++) 
	{
		mDescriptor=LinkRxLoopDescriptor[it%LinkRxLoopMany];
#ifdef MEMORYREAD
        //
		// read the next descriptor
		//
        MyMemoryRead(mDescriptor, descriptor, dsize);		
#else
        descriptor=(unsigned int *)MyMemoryPtr(mDescriptor);
#endif
        //
		// descriptor is ready, we have a new packet
		//
        if(RxDescriptorDone(descriptor)) 
		{
#ifdef MEMORYREAD
            //
		    // read the next descriptor
		    //
            MyMemoryRead(mDescriptor, descriptor, dsize);
#endif

		
			notdone=0;
			behind++;
			scount=0;
#ifdef FASTER
			if((it%100)==0)
			{
				UserPrint(" %d",it);
			}
#endif         
            //
			// dump packet contents
			//
            if(ndump>0) 
			{
				UserPrint("\n");
                LinkRxDump(descriptor, ndump, it%LinkRxLoopMany);
            }
			//
			// extract stats
			//
			LinkRxStatExtract(descriptor,it);
			//
			// reset the descriptor so that we can use it again
			//
			RxDescriptorReset(descriptor);
#ifdef MEMORYREAD
			//
			// copy it back to the shared memory
			//
			MyMemoryWrite(mDescriptor,descriptor,dsize);
#endif
			//
			// need to queue the descriptor with the hardware
			//
            if(LinkRxDescriptorFifo)
            {
	            DeviceReceiveDescriptorPointer(mDescriptor);
            }
        } 
		//
		// descriptor not ready
		//
		else 
		{
			if(stop)
			{
				scount++;
				if(scount>200)
				{
				    break;
				}
			}
			it--;
			//
			// sleep every other time, need to keep up with fast rates
			//
			if(notdone>100)
			{
			    UserPrint(".");
//			    MyDelay(1);
				notdone=0;
			}
			else
			{
				notdone=1;      // this makes sure we never sleep
			}
        }
		//
		// check for message from cart telling us to stop
		// this is the normal terminating condition
		//
		pass++;
		if(pass>100)
		{
			if(done!=0)
			{
				isdone=(*done)();
				if(isdone!=0)
				{
					UserPrint(" %d Stop",it);
					if(stop==0)
					{
						stop=1;
						scount=0;
						behind=0;
					}
					//
					// immediate stop
					//
					if(isdone==2)
					{
						break;
					}
	//				break;
				}
			}
			pass=0;
		    //
		    // check for timeout
		    // rare terminating condition when cart crashes or disconnects
		    //
		    ctime=TimeMillisecond();
		    if(timeout>0 && ((endTime>startTime && (ctime>endTime || ctime<startTime)) || (endTime<startTime && ctime>endTime && ctime<startTime)))
		    {
			    UserPrint("%d Timeout",it);
			    break;
		    }
		}
    } 
    //
	// cleanup
	//
	DeviceReceiveDisable();

	LinkRxLoopDestroy(0);

    //
	// do data verify if requested
	//
	UserPrint(" %d\n",it);

	LinkRxStatFinish();

    return;
}
Пример #3
0
int DescriptorLinkTxSetup(int *rate, int nrate, int ir,
    unsigned char *bssid, unsigned char *source, unsigned char *destination,
    int numDescPerRate, int dataBodyLength,
    int retries, int antenna, int broadcast, int ifs,
	int shortGi, unsigned int txchain, int naggregate,
	unsigned char *pattern, int npattern)
{
    int it;
    unsigned int    antMode = 0;
    int queueIndex;
	int dcuIndex;
    int wepEnable;
	static unsigned char bcast[]={0xff,0xff,0xff,0xff,0xff,0xff};
	unsigned char *duse;
	unsigned char *ptr;

	DescriptorLinkRxOptions();
	DescriptorLinkTxOptions();

	TxStatusCheck();
    DeviceTransmitDisable(0xffff);
	DeviceReceiveDisable();
	TxStatusCheck();
    //
    // do we still need these?
    //
	wepEnable=0;
	queueIndex=0;
	dcuIndex=0;
    //
	// Cleanup any descriptors and memory used in previous iteration
	// Make sure you do these first, since they actually destroy everything.
	//
	LinkTxLoopDestroy();
	DescriptorLinkRxLoopDestroy();
    //
    // remember some parameters for later use when we queue packets
    //
    TxChain=txchain;
    ShortGi=shortGi;
    Broadcast=broadcast;
    Retry=retries;

    PacketMany= numDescPerRate;
    //
    // adjust aggregate parameters to the range [1,MAGGREGATE]
    //
	if(naggregate<=1)
	{
		AggregateMany=1;
		AggregateBar=0;
	}
	else
	{
		AggregateMany=naggregate;
		if(AggregateMany>MAGGREGATE)
		{
			AggregateMany=MAGGREGATE;
		}
        //
        // determine if we should do special block acknowledgement request (BAR) packet
        //
        if(Broadcast)
        {
		    AggregateBar=0;
        }
        else
        {
		    AggregateBar=0;                 // never do this
        }
        //
        // treat the incoming number of packets as the total
        // but we treat it internally as the number of aggregate groups, so divide
        //
        if(PacketMany>0)
        {
            PacketMany/=AggregateMany;
            if(PacketMany<=0)
            {
                PacketMany=1;
            }
        }
        //
        // enforce maximun aggregate size of 64K
        //
        if(AggregateMany*dataBodyLength>65535)
        {
            AggregateMany=65535/dataBodyLength;
            UserPrint("reducing AggregateMany from %d to %d=65535/%d\n",naggregate,AggregateMany,dataBodyLength);
        }
	}
    //
	// Count rates and make the internal rate arrays.
	//
    RateMany = nrate;
	for(it=0; it<RateMany; it++)
	{
		Rate[it]=rate[it];
	}
	//RateCount(rateMask, rateMaskMcs20, rateMaskMcs40, Rate);
	//
	// Remember other parameters, for descriptor queueing.
	//
	if(PacketMany>0)
	{
        InterleaveRate=ir;
	}
	else
	{
		InterleaveRate=1;
	}
    //
    // If the user asked for an infinite number of packets, set to a million
    // and force interleave rates on.
    //
    if(PacketMany<=0)
    {
//        PacketMany=1000000;
		LinkTxMany= -1;
        InterleaveRate=1;
    }
	//
	// if in tx100 mode, we only do one packet at the first rate
	//
	if(ifs==0)		
	{
		//
		// tx100
		//
		RateMany=1;
		PacketMany=1;
        Tx100Packet = 1;
		Broadcast=1;
	}
	else if(ifs>0)
	{
		//
		// tx99
		//
        Tx100Packet = 0;
		Broadcast=1;
	}
	else
	{
		//
		// regular
		//
        Tx100Packet = 0;
	}
    //
    // If broadcast use the special broadcast address. otherwise, use the specified receiver
    //
	if(Broadcast)
	{
		duse=bcast;
	}
	else
	{
		duse=destination;
	}
	if(!Broadcast && !DeafMode)
	{
		//
		// Make rx descriptor
		//
		DescriptorLinkRxLoopCreate(MQUEUE);
	}
    //
    // calculate the total number of packets we expect to send.
    // and then figure out how many we should do in each batch.
    // if the number of packets is small, we may be able to do them in one batch.
    //
	if(PacketMany>0)
	{
		LinkTxMany=PacketMany*RateMany*(AggregateMany+AggregateBar);
		if(PacketMany>0 && LinkTxMany<=MQUEUE)
		{
			LinkTxBatchMany=LinkTxMany;                         // do in one batch
			LinkTxDescriptorMany=LinkTxMany;
		}
		else
		{
			LinkTxBatchMany=MQUEUE/2;		                    // do in two or more batches
			LinkTxBatchMany/=(AggregateMany+AggregateBar);		// force complete Aggregate into a batch
			LinkTxBatchMany*=(AggregateMany+AggregateBar);	
			LinkTxDescriptorMany=2*LinkTxBatchMany;
		}
	}
	else
	{
		LinkTxBatchMany=MQUEUE/2;		                    // do in two or more batches
		LinkTxBatchMany/=(AggregateMany+AggregateBar);		// force complete Aggregate into a batch
		LinkTxBatchMany*=(AggregateMany+AggregateBar);	
		LinkTxDescriptorMany=2*LinkTxBatchMany;
	}
    //
    // now calculate the number of tx responses we expect
    //
    // Merlin and before return a response for every queued descriptor but only
    // the terminating descriptor for an aggregate has the done bit set. Skip the others in LinkTxComplete().
    //
    // Osprey only returns status for the terminating packet of aggregates, so there are
    // fewer status descriptors than control descriptors.
    //
	if(LinkTxAggregateStatus || AggregateMany<=1 || PacketMany<=0)
	{
		LinkTxStatusMany=LinkTxMany;
	}
	else
	{
		LinkTxStatusMany=0;
		for(it=0; it<RateMany; it++)
		{
			if(IS_LEGACY_RATE_INDEX(Rate[it]))
			{
				//
				// legacy rates don't support aggrgegates
				//
				LinkTxStatusMany+=(PacketMany*(AggregateMany+AggregateBar));
			}
			else
			{
				//
				// only 1 status message is returned for aggregates
				//
				LinkTxStatusMany+=(PacketMany*(1+AggregateBar));
			}
		}
	}
    UserPrint("TxMany=%d TxStatusMany=%d TxBatchMany=%d TxDescriptorMany=%d\n",LinkTxMany,LinkTxStatusMany,LinkTxBatchMany,LinkTxDescriptorMany);
    //
	// create the required number of descriptors
	//
    LinkTxLoopDescriptor[0] = MemoryLayout(LinkTxDescriptorMany * TxDescriptorSize());
    if(LinkTxLoopDescriptor[0]==0) 
	{
        UserPrint("txDataSetup: unable to allocate client memory for %d control descriptors\n",LinkTxDescriptorMany);
        return -1;
    }
	for(it=0; it<LinkTxDescriptorMany; it++)
	{
		LinkTxLoopDescriptor[it]=LinkTxLoopDescriptor[0]+it*TxDescriptorSize();
	}
    //
    // make status descriptors
    //
    // for pre-Osprey chips, these pointers point to the regular tx descriptor
    //
	if(LinkTxStatusLoopCreate(LinkTxDescriptorMany))
	{
		return -1;
	}
	//
	// use PN9 data if no pattern is specified
	//
	UserPrint("npattern=%d pattern=%x\n",npattern,pattern);
	if(npattern<=0)
	{
		pattern=PN9Data;
		npattern=sizeof(PN9Data);
	}
	UserPrint("npattern=%d pattern=%x\n",npattern,pattern);
 		ptr=(unsigned char *)pattern;
		if(ptr!=0)
		{
			UserPrint("pattern: ");
			for(it=0; it<npattern; it++)
			{
				UserPrint("%2x ",ptr[it]);
			}
			UserPrint("\n");
		}
    //
	// setup the transmit packets
	//
    if(AggregateMany>1)
    {
	    for(it=0; it<AggregateMany; it++)
	    {
		    LinkTxAggregatePacketCreate(dataBodyLength, pattern, npattern,
			    bssid, source, duse, Tx100Packet, wepEnable, it, AggregateBar);              
		    if(LinkTxLoopBuffer(it)==0) 
		    {
			    UserPrint("txDataSetup: unable to allocate memory for packet %d\n",it);
			    return -1;
		    }
	    }
        //
	    // setup the BAR packet
	    //
        if(AggregateBar>0)
	    {
		    LinkTxBarCreate(source, duse);              
		    if(LinkTxBarBuffer==0) 
		    {
			    UserPrint("txDataSetup: unable to allocate memory for bar packet\n");
			    return -1;
		    }
	    }
    }
    else
    {
		LinkTxPacketCreate(dataBodyLength,pattern,npattern,
			bssid, source, duse, Tx100Packet,wepEnable);              
		if(LinkTxLoopBuffer(0)==0) 
		{
			UserPrint("txDataSetup: unable to allocate memory for packet\n");
			return -1;
		}
    }

    DeviceTransmitRetryLimit(dcuIndex,retries);	

	//
	// regular mode
	//
	if(ifs<0)
	{
		DeviceTransmitRegularData();
		DeafMode=0;
	}
    //
	// tx99 mode
	//
	else if(ifs>0)
	{
		DeviceTransmitFrameData(ifs);
		DeafMode=1;
	}
	//
	// tx100 mode
	//
	else
	{
		DeviceTransmitContinuousData();
		DeafMode=1;
	}

//#ifdef BADBADBAD	// i don't think this works
	DeviceReceiveDeafMode(DeafMode);
//#endif

	DeviceBssIdSet(bssid);
	DeviceStationIdSet(source);
    //
	// make the first batch of transmit control descriptors, but don't queue them yet
	//
	LinkTxBatchNext(0,0);
	LinkTxBatchNext(LinkTxBatchMany,0);

    return 0;
}
Пример #4
0
// Added VSG sync detection 
void LinkRxComplete_vsgSync(int timeout, 
	int ndump, unsigned char *dataPattern, int dataPatternLength, int (*done)(), int chainMask)
{
    unsigned int startTime, endTime, ctime;
	int it, ii;
	int notdone;
	int stop;
	int behind;
	int scount;
	unsigned int mDescriptor;			// address of descriptor in shared memory
#ifdef MEMORYREAD
	unsigned int descriptor[MDESCRIPTOR+30];	// private copy of current descriptor
#else
    unsigned int *descriptor;
#endif
	int pass;
    int dsize;
	int isdone;

    int ss; 
    int rssic[MCHAIN], rssie[MCHAIN], rssi; 
    #define MSPECTRUM 1024 
    int nspectrum,spectrum[MSPECTRUM]; 
    int ndata; 
    unsigned char data[MBUFFER]; 
    int sscount=0; 
    unsigned int vsgSync = 0;

	pass=0;
	dataPatternLength=0;
	dataPattern=0;
	scount=0;
    dsize=RxDescriptorSize();
    //
	// Loop timeout condition.
	// This number can be large since it is only used for
	// catastrophic failure of cart. The normal terminating
	// condition is a message from cart saying STOP.
	//
    startTime=TimeMillisecond();
	ctime=startTime;
	if(timeout<=0)
	{
		timeout=2*60*1000;             // 2 mins
	}
	endTime=startTime+timeout;		
	//
	// set pointer to first descriptor
	//
//    mDescriptor = LinkRxLoopFirst(0);
	//
	// keep track of how many times we look and the descriptor is not done
	// this is used to control the sleep interval
	//
	notdone=0;
	stop=0;
	behind=0;
	//
	// loop looking for descriptors with received packets.
	//
	for(it = 0; ; it++) 
	{
		mDescriptor=LinkRxLoopDescriptor[it%LinkRxLoopMany];
#ifdef MEMORYREAD
        //
		// read the next descriptor
		//
        MyMemoryRead(mDescriptor, descriptor, dsize);		

#else
        descriptor=(unsigned int *)MyMemoryPtr(mDescriptor);
#endif
        //
		// descriptor is ready, we have a new packet
		//
       
        if(RxDescriptorDone(descriptor)) 
		{
#ifdef MEMORYREAD
            //
		    // read the next descriptor
		    //
            MyMemoryRead(mDescriptor, descriptor, dsize);
#endif
			notdone=0;
			behind++;
			scount=0;
#ifdef FASTER
			if((it%SASAMPLINGS)==0)   
			{
				UserPrint(" %d",it);
			}
#endif         

            if(_LinkRxSpectralScan) 
            { 
                ss=Ar9300RxDescriptorSpectralScan(descriptor); 
                if(ss) 
                { 
                    // 
                    // extract rssi and evm measurements. we need these for both good and bad packets 
                    // 
                    rssi=RxDescriptorRssiCombined(descriptor); 
                    if(rssi&0x80) 
                    { 
                        rssi|=0xffffff00; 
                    } 
                    rssic[0]=RxDescriptorRssiAnt00(descriptor); 
                    rssic[1]=RxDescriptorRssiAnt01(descriptor); 
                    rssic[2]=RxDescriptorRssiAnt02(descriptor); 
                    rssie[0]=RxDescriptorRssiAnt10(descriptor); 
                    rssie[1]=RxDescriptorRssiAnt11(descriptor); 
                    rssie[2]=RxDescriptorRssiAnt12(descriptor); 


                    for(ii=0; ii<3; ii++) 
                    { 
                        if(rssic[ii]&0x80) 
                        { 
                           rssic[ii]|=0xffffff00; 
                        } 
                        if(rssie[ii]&0x80) 
                        { 
                            rssie[ii]|=0xffffff00; 
                        } 
                    }

                    ndata=RxDescriptorDataLen(descriptor); 
                    MyMemoryRead(LinkRxLoopBuffer[it%LinkRxLoopMany], (unsigned int *)data, ndata); 
                    nspectrum=Ar9300SpectralScanProcess(data,ndata,spectrum,MSPECTRUM); 

                    if(_LinkRxSpectralScanFunction!=0) 
                    { 
                        (*_LinkRxSpectralScanFunction)(rssi,rssic,rssie,MCHAIN,spectrum,nspectrum); 
                    }
                    // 
                    // extract stats 

                    if (!vsgSync)
                    {
                        vsgSync = ((rssic[0] >= ISSM80DBM_THRESHOLD) && (chainMask & (1 << 0)) || 
                                   (rssic[1] >= ISSM80DBM_THRESHOLD) && (chainMask & (1 << 1)) ||
                                   (rssic[2] >= ISSM80DBM_THRESHOLD) && (chainMask & (1 << 2)) );
                    }
                    else
                    {
                        LinkRxStatSpectralScanExtract(descriptor,it); 
                    
                        sscount++; 
	                //UserPrint("ss_cnt:rssic:mask %d, %d, %d, %d\n",sscount, rssic[0], rssic[1], chainMask);
                    }

                    if(sscount >= SASAMPLINGS)   
                    { 
                        break; 
                    } 
                } 
            } 
            else 
            { 
                // dump packet contents
	    		//
                if(ndump>0) 
			    {
				    UserPrint("\n");
                    LinkRxDump(descriptor, ndump, it%LinkRxLoopMany);
                }
			    //
			    // extract stats
			    //
			    LinkRxStatExtract(descriptor,it);

            }
			//
			// reset the descriptor so that we can use it again
			//
			RxDescriptorReset(descriptor);
#ifdef MEMORYREAD
			//
			// copy it back to the shared memory
			//
			MyMemoryWrite(mDescriptor,descriptor,dsize);
#endif
			//
			// need to queue the descriptor with the hardware
			//
            if(LinkRxDescriptorFifo)
            {
	            DeviceReceiveDescriptorPointer(mDescriptor);
            }
        } 
		//
		// descriptor not ready
		//
		else 
		{
			if(stop)
			{
				scount++;
				if(scount>200)
				{
				    break;
				}
			}
			it--;
			//
			// sleep every other time, need to keep up with fast rates
			//
			if(notdone>100)
			{
			    UserPrint(".");
//			    MyDelay(1);
				notdone=0;
			}
			else
			{
				notdone=1;      // this makes sure we never sleep
			}
        }
		//
		// check for message from cart telling us to stop
		// this is the normal terminating condition
		//
		pass++;
		if(pass>100)
		{
			if(done!=0)
			{
				isdone=(*done)();
				if(isdone!=0)
				{
					UserPrint(" %d Stop",it);
					if(stop==0)
					{
						stop=1;
						scount=0;
						behind=0;
					}
					//
					// immediate stop
					//
					if(isdone==2)
					{
						break;
					}
	//				break;
				}
			}
			pass=0;
		    //
		    // check for timeout
		    // rare terminating condition when cart crashes or disconnects
		    //
		    ctime=TimeMillisecond();

#if 1
            if( timeout>0 && ((endTime>startTime && (ctime>endTime || ctime<startTime)) || (endTime<startTime && ctime>endTime && ctime<startTime)))
		    {

			    UserPrint("%d Timeout",it);
			    break;
		    }
#endif
		}
    } 
    //
	// cleanup
	//

    if(_LinkRxSpectralScan) 
    { 
        //DeviceSpectralScanDisable(); 
        Ar9300SpectralScanDisable();
    } 

	DeviceReceiveDisable();

	LinkRxLoopDestroy(0);

    //
	// do data verify if requested
	//
	UserPrint(" %d\n",it);

	LinkRxStatFinish();

    return;
}
Пример #5
0
//
// run the transmitter
//
// (*ison)() is called when the transmitter is guaranteed to be on (first descriptor returned done)
//
// (*done)() is called to check if this function should stop early
//
int DescriptorLinkTxComplete(int timeout, int (*ison)(), int (*done)(), int chipTemperature, int calibrate)
{
	int it;
    unsigned int startTime, endTime, ctime, failTime;
	int failRetry;
	int notdone;
	unsigned int mDescriptor;			    // address of descriptor in shared memory
	unsigned int descriptor[MDESCRIPTOR];	// private copy of current descriptor
    int cindex;                             // index of the corresponding control descriptor
	unsigned int *cd,cdescriptor[MDESCRIPTOR];	// private copy of current control descriptor
    int pass;
    int dsize;
//    int behind;
    int batch, lbatch;
	int temperature;
	int doTemp;

    LinkTxStatClear();
    lbatch=0;
    pass=0;
	doTemp=0;
    dsize=TxDescriptorSize();
    //
	// Loop timeout condition.
	// This number can be large since it is only used for
	// catastrophic failure of cart. The normal terminating
	// condition is a message from cart saying STOP.
	//
    startTime=TimeMillisecond();
	ctime=startTime;
//	if(timeout<=0)
//	{
//		timeout=60*60*1000;         // one hour
//	}
	endTime=startTime+timeout; 
	if(Tx100Packet)
	{
		failTime=endTime;
	}
	else
	{
		failTime=startTime+(5*1000);	// 5 seconds
	}
	failRetry=0;
	//
	// set pointer to first descriptor
	//
    mDescriptor = LinkTxLoopFirst();
	//
	// keep track of how many times we look and the descriptor is not done
	// this is used to control the sleep interval
	//
	notdone=0;
//	tlast=0;
	//
	// if this is the first descriptor, announce that the transmitter is really on
	//
	if(ison!=0)
	{
		(*ison)();
	}
	//
	// loop looking for descriptors with transmitted packets.
	// terminate either when we reach a null pointer (Merlin and prior) or
	// when we've done the specified number of packets (Osprey)
	//
	for(it=0; (LinkTxStatusMany<=0 || it<LinkTxStatusMany); it++) 
	{
        //
		// read the next descriptor
		//
		mDescriptor=LinkTxLoopDescriptorStatus[it%LinkTxDescriptorMany];
        DeviceMemoryRead(mDescriptor, descriptor, dsize);
		//
		// skip ahead for aggregates. 
		//
        if(LinkTxAggregateStatus)
        {
		    if(TxDescriptorAggregate(descriptor) && TxDescriptorMoreAgg(descriptor))
		    {
//			    UserPrint("A");
                continue;
		    }
        }
        //
		// is it done?
		//
		if(TxDescriptorDone(descriptor))	
		{
            cindex=LinkTxQueued[it%LinkTxDescriptorMany];
            batch=cindex/LinkTxBatchMany;
//            UserPrint("(%d %d %d)",it,cindex,batch);

            DeviceMemoryRead(mDescriptor, descriptor, dsize);
            //
            // get the control descriptor too
            //
            if(LinkTxSplitDescriptor)
            {
                DeviceMemoryRead(LinkTxLoopDescriptor[cindex%LinkTxDescriptorMany], cdescriptor, TxDescriptorSize());	
                cd=cdescriptor;
            }
            else
            {
                cd=descriptor;
            }
#ifdef UNUSED
            behind=(TxDescriptorPointerGet()-mDescriptor)/dsize;
            if(behind>10)
            {
                UserPrint(" -%d",behind);
            }
#endif
			notdone=0;
			if((it%100)==0)
			{
				UserPrint(" %d",it);
			}
			LinkTxStatExtract(descriptor,cd,AggregateMany,it);
			//
			// clear the status packet
			//
            if(LinkTxSplitDescriptor)
            {
	            TxDescriptorStatusSetup(descriptor);
		        DeviceMemoryWrite(LinkTxLoopDescriptorStatus[it%LinkTxDescriptorMany],descriptor,TxDescriptorStatusSize());
            }
            //
            // Do we need to queue the next batch of descriptors?
            //
			if(batch>lbatch)
			{
//                UserPrint("\n%d %d %d %d\n",it,cindex,lbatch,batch);
				LinkTxBatchNext((batch+1)*LinkTxBatchMany,1);
                lbatch=batch;
			}
		}
		//
		// descriptor isn't done
		//
		else 
		{
			it--;
			//
			// sleep every other time, need to keep up with fast rates
			//
			if(notdone>100)
			{
			    UserPrint(".");
//			    MyDelay(1);
				notdone=0;
			}
			else
			{
				notdone++;
			}
        }
		//
		// check temperature
		//
		if(doTemp>10)
		{
			temperature=DeviceTemperatureGet(0);
			//
			// are we supposed to terminate on reaching a certain chip temperature?
			//
			if(chipTemperature>0)
			{
				if(temperature<=chipTemperature)
				{
					UserPrint(" %d Temperature %d <= %d",it,temperature,chipTemperature);
					break;
				}
			}
			doTemp=0;
		}
		doTemp++;
		//
		// check for message from cart telling us to stop
		// this is the normal terminating condition
		//
		pass++;
		if(pass>100)
		{
			if(done!=0)
			{
				if((*done)())
				{
					UserPrint(" %d Stop",it);
    				break;
				}
			}
			pass=0;
		    //
		    // check for timeout
		    // rare terminating condition when cart crashes or disconnects
		    //
		    ctime=TimeMillisecond();
		    if(timeout>0 && ((endTime>startTime && (ctime>endTime || ctime<startTime)) || (endTime<startTime && ctime>endTime && ctime<startTime)))
		    {
			    UserPrint(" %d Timeout",it);
			    break;
		    }
			//
			// check for failure to start
			//
			if(it<=0)
			{
				if((failTime>startTime && (ctime>failTime || ctime<startTime)) || (failTime<startTime && ctime>failTime && ctime<startTime))
				{
					UserPrint("Failed to start transmit.\n");
					if(failRetry==0)
					{
						UserPrint("Trying to restart.\n");
						DescriptorLinkTxStart();
						failTime=startTime+(5*1000);	// 5 seconds
						failRetry++;
						//
						// set pointer to first descriptor
						//
						mDescriptor = LinkTxLoopFirst();
					}
					else
					{
						ResetForce();
						break;
					}
				}
			}
		}
    } 

    TxStatusCheck();

	DeviceTransmitDisable(0xffff);
	DeviceReceiveDisable();

	LinkTxLoopDestroy();
	DescriptorLinkRxLoopDestroy();
	//
	// finish throughput calculation
	//
	LinkTxStatFinish();

    TxStatusCheck();

	UserPrint(" %d Done\n",it);

    return 0;
}