Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
OTLLCTest.c
/* |
File: OTLLCTest.c |
Contains: Simple app write or receive 8022 Ethernet packets using a multicast address |
This program implements both a sender and receiver such that both sides |
open an 802.2 Ethernet endpoint. The user can then select whether to run the |
program as a sender or receiver. If implemented as a receiver, the endpoint |
is bound, and the multicast option is turned on. The receiver waits in a |
spin loop for a specified period of time before quitting. The receiver will process |
all incoming ethernet packets destined to the endpoint for the specified |
protocol. Upon receipt, the program checks to see whether the packet was sequential |
to the previous packet. A collection of global counter maintains the number of |
inOrder, outOfOrder packets, and the number of packets reads resulting in an error, |
plus the number of packets which come in back to back while in the handler. |
Note the this sample turns on the rawmode option so that the handler will be passed the |
14 byte 802.2 header. |
Also note that the sender may also implement the rawmode option so that it can also |
fill in the header bytes itself. If this is done, then the buffer needs to |
be enlarged to include these additional bytes. These additional bytes will not affect |
the maximum tsdu size since the tsdu size is the i-frame limit and does not include |
the header size. |
The sender process, sends 10005 x 1500 byte packets as fast as possible. The user |
can select to to turn on AckSends mode where the packet is handed to OT and not |
released until OT sends the information to the lower layer. |
Written by: Rich Kubota |
Copyright: Copyright © 1993-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
7/22/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
01/98 added threshold timer to control how often WNE gets called. |
01/98 fixed sample so that you can send in raw mode and receive in |
regular mode or vice versa and things work. |
Fixed problem that when setting the max data packet size to |
1500 bytes and are sending data in regular mode, adjust the |
unitdata.data.len field to account for the fact that the |
endpoint will insert the LLC header and the SNAP header. |
For raw mode, this is not an issue. Added feature to 1. allow |
the program to be re-used without having to quit 2. ask user for |
the drivername to use e.g. 'enet0, 'enet1', etc., |
01/98 modified the sample to use the revised NegotiateRawModeOption |
code which returns the template type. The Mentat template driver |
returns an additional 24 bytes of info at the beginning of the |
raw data packet. |
01/98 modified DoBind so that one can use either a regular LLC to SNAP |
endpoint. |
10/97 fixed bug in OT 8022 module which prevented more than 1483 data |
bytes from being sent in raw mode. Requires OT 1.3 to send > 1483 |
data bytes with endpoint in rawdata mode. |
*/ |
#include <stdio.h> |
#include <Types.h> |
#include <Memory.h> |
#include <Resources.h> |
#include <Events.h> |
#include <OpenTransport.h> // open transport files |
#include <OpenTptLinks.h> |
#include <OpenTptAppleTalk.h> |
#include <OpenTptConfig.h> |
#include <Time.h> |
#include <Errors.h> |
#include <String.h> |
#include "OTLLCTest.h" |
#include "NegotiateRawModeSample.h" |
// Comment out the following line if synchronous sends desired. |
#define __ASYNCSEND__ 1 |
//----------------------------------------------------------------------------------------- |
// Globals |
//----------------------------------------------------------------------------------------- |
EndpointRef gEndpoint; |
OSStatus gstatus; |
UInt32 gFlags; |
UInt32 gNumBack, gNumFore; |
UInt32 gPacketsRead; |
UInt32 gBackToBackPackets; |
UInt32 gInOrder; |
UInt32 gOutOfOrder; |
UInt32 gCounter; |
UInt32 gNumDataEvents; |
UInt32 gReadErrors; |
UInt32 gTemplateType; |
UInt32 gNumMemErrs; |
UInt32 gTimerThreshold; |
UInt8 *gBuffer; |
UInt8 *gDummyBuffer; |
struct T8022Address gAddr; |
UInt8 gmcAddr[k48BitAddrLength] = {MCASTADDR0,MCASTADDR1,MCASTADDR2,MCASTADDR3,MCASTADDR4,MCASTADDR5}; |
PacketBuffer gPacket; |
UInt8 gFlag1; |
Boolean gDone; |
Boolean gAbort; |
//----------------------------------------------------------------------------------------- |
// Prototypes |
//----------------------------------------------------------------------------------------- |
extern OSStatus OTSetMemoryLimits(size_t growSize, size_t mazSize); |
OSStatus DoBind(); |
OSStatus DoAddMulticast(EndpointRef ep, unsigned char *mcAddr); |
OSStatus DoRemoveMulticast(EndpointRef ep, unsigned char *mcAddr); |
void WriteApplIntro(void); |
UInt32 GetYesNoOption(void); |
UInt32 GetUserOption(void); |
Boolean CanDoMDATAMode(EndpointRef ep); |
void DoOTLLCWriteTest(void); |
Boolean DoSendPacket(EndpointRef ep); |
void DoOTLLCReadTest(void); |
OSStatus DoReadPacket(EndpointRef ep, UInt8 *mainBuffer); |
pascal void LLCEventHandler(void* ref, OTEventCode event, OTResult result, void* cookie); |
void CallWNE(void); |
void MyIdle(void); |
void DoValueBreak(long value, const char* message); |
void GetDriverName(char *name); |
void SetTimerThreshold(void); |
void PrintAppleTalkPortName(void); |
void ListEnetDrivers(void); |
/******************************************************************************* |
** DoBindENET |
********************************************************************************/ |
OSStatus DoBind(void) |
{ |
OSStatus osstatus; |
TBind requestInfo; |
TBind responseInfo; |
UInt32 i; |
gAddr.fAddrFamily = AF_8022; |
for (i = 0; i < k48BitAddrLength; i++) |
gAddr.fHWAddr[i] = 0x00; |
gAddr.fSAP = TESTSAP; |
if (TESTSAP == 0xAA) |
{ |
// set SNAP fields; |
gAddr.fSNAP[0] = MYSNAP0; // set these values in the interface file OTLLCTest.h |
gAddr.fSNAP[1] = MYSNAP1; |
gAddr.fSNAP[2] = MYSNAP2; |
gAddr.fSNAP[3] = MYSNAP3; |
gAddr.fSNAP[4] = MYSNAP4; |
requestInfo.addr.len = k8022SNAPAddressLength; |
} |
else |
{ |
requestInfo.addr.len = k8022BasicAddressLength; |
} |
// finish bind information |
requestInfo.addr.buf = (UInt8 *)&gAddr; |
requestInfo.addr.maxlen = 0; |
requestInfo.qlen = 0; |
responseInfo.addr.buf = (UInt8 *)&gAddr; |
responseInfo.addr.len = 0; |
responseInfo.addr.maxlen = k8022SNAPAddressLength; |
responseInfo.qlen = 0; |
osstatus = OTBind(gEndpoint, &requestInfo, &responseInfo); |
if (osstatus == kOTNoError) |
SetEPBoundFlag(gFlags); |
return osstatus; |
} |
/******************************************************************************* |
** DoAddMulticast |
********************************************************************************/ |
OSStatus DoAddMulticast(EndpointRef ep, unsigned char *mcAddr) |
{ |
OSStatus osstatus = noErr; |
TOptMgmt req; |
UInt8 reqOpt[64]; |
req.opt.buf = reqOpt; |
req.flags = T_NEGOTIATE; |
((TOption*)reqOpt)->level = LNK_TPI; |
((TOption*)reqOpt)->name = OPT_ADDMCAST; |
((TOption*)reqOpt)->len = kOTOptionHeaderSize + k48BitAddrLength; |
((TOption*)reqOpt)->status = 0; |
memcpy(((TOption*)reqOpt)->value, mcAddr, k48BitAddrLength); |
req.opt.len = kOTOptionHeaderSize + k48BitAddrLength; |
req.opt.maxlen = sizeof(reqOpt); |
if ( (osstatus = OTOptionManagement(ep, &req, &req)) != kOTNoError ) |
fprintf(stderr, "DoAddMulticast - OptionManagement Returned %d\n", osstatus); |
else |
{ |
if (((TOption*)reqOpt)->status != T_SUCCESS) |
{ |
fprintf(stderr, "DoAddMulticast - failed Status = %d\n", ((TOption*)reqOpt)->status); |
osstatus = -1; |
} |
else |
{ |
fprintf(stderr, "DoAddMulticast - was successful\n"); |
SetMCastActiveFlag(gFlags); |
} |
} |
return osstatus; |
} |
/******************************************************************************* |
** DoRemoveMulticast |
********************************************************************************/ |
OSStatus DoRemoveMulticast(EndpointRef ep, unsigned char *mcAddr) |
{ |
OSStatus osstatus = noErr; |
TOptMgmt req; |
UInt8 reqOpt[64]; |
req.opt.buf = reqOpt; |
req.flags = T_NEGOTIATE; |
((TOption*)reqOpt)->level = LNK_TPI; |
((TOption*)reqOpt)->name = OPT_DELMCAST; |
((TOption*)reqOpt)->status = 0; |
((TOption*)reqOpt)->len = kOTOptionHeaderSize + k48BitAddrLength; |
memcpy(((TOption*)reqOpt)->value, mcAddr, k48BitAddrLength); |
req.opt.len = kOTOptionHeaderSize + k48BitAddrLength; |
req.opt.maxlen = sizeof(reqOpt); |
if ( (osstatus = OTOptionManagement(ep, &req, &req)) != kOTNoError ) |
fprintf(stderr, "\nDoRemoveMulticast failed - OptionManagement Returned %d.", osstatus); |
else |
{ |
if (((TOption*)reqOpt)->status != T_SUCCESS) |
{ |
fprintf(stderr, "nDoRemoveMulticast - failed Status = %d\n", ((TOption*)reqOpt)->status); |
osstatus = -1; |
} |
else |
{ |
fprintf(stderr, "nDoRemoveMulticast - was successful\n"); |
SetMCastActiveFlag(gFlags); |
} |
} |
return osstatus; |
} |
void WriteApplIntro(void) |
{ |
fprintf(stderr, "\nEthernet 802.2 LLC Test program v1.0\n"); |
fprintf(stderr, "\nThis test application sets the system"); |
fprintf(stderr, "\ninto send or receive mode.\n"); |
fprintf(stderr, "\nThe send portion of this program sets the Ethernet"); |
fprintf(stderr, "\ndriver to use a multicast address, then sends 10000"); |
fprintf(stderr, "\n- 1500 byte packets out the wire.\n"); |
fprintf(stderr, "\nThe receive portion of this program sets the Ethernet"); |
fprintf(stderr, "\ndriver to use a multicast address, then waits for the"); |
fprintf(stderr, "\n10000 - 1500 byte packets or times out after 30 seconds.\n"); |
fprintf(stderr, "\n\nUsing SAP address %d.\n", TESTSAP); |
} |
UInt32 GetYesNoOption(void) |
{ |
UInt32 result; |
char selection[32]; |
Boolean done; |
fprintf(stdout, "\n Enter Y - To accept option"); |
fprintf(stdout, "\n Enter N - To decline option"); |
fprintf(stdout, "\n Enter Q - To quit"); |
fprintf(stdout, "\nYour selection -> "); |
fflush(stdout); |
done = false; |
do |
{ |
scanf("%s", selection); |
switch (selection[0]) |
{ |
case 'y': |
case 'Y': |
result = kAcceptOption; |
done = true; |
break; |
case 'n': |
case 'N': |
result = kDeclineOption; |
done = true; |
break; |
case 'q': |
case 'Q': |
result = kQuitTest; |
done = true; |
break; |
default: |
fprintf(stdout, "\nInvalid entry - %c, try again -> ", selection); |
fflush(stdout); |
break; |
} |
} while (!done); |
fflush (stdout); |
return result; |
} |
UInt32 GetUserOption(void) |
{ |
UInt32 result; |
char selection[32]; |
Boolean done; |
fprintf(stdout, "\nSelect the type of test to run"); |
fprintf(stdout, "\nMake sure that the receive program is already launched"); |
fprintf(stdout, "\n Enter S - Send test packets"); |
fprintf(stdout, "\n Enter R - Receive test packets"); |
fprintf(stdout, "\n Enter q - quit"); |
fprintf(stdout, "\nYour selection -> "); |
fflush(stdout); |
done = false; |
do |
{ |
scanf("%s", selection); |
switch (selection[0]) |
{ |
case 'r': |
case 'R': |
result = kReceiveTest; |
done = true; |
break; |
case 's': |
case 'S': |
result = kSendTest; |
done = true; |
break; |
case 'q': |
case 'Q': |
result = kQuitTest; |
done = true; |
break; |
default: |
fprintf(stdout, "\nInvalid entry - %c, try again -> ", selection); |
fflush(stdout); |
break; |
} |
} while (!done); |
fflush (stdout); |
return result; |
} |
/* |
CanDoMDATAMode gets the endpoint info and checks whether the T_CAN_SUPPORT_MDATA bit is |
set in the endpoint info flag field and returns true if so, false otherwise. |
*/ |
Boolean CanDoMDATAMode(EndpointRef ep) |
{ |
TEndpointInfo info; |
OSStatus err; |
Boolean result; |
err = OTGetEndpointInfo(ep, &info); |
if (err != kOTNoError) |
result = false; |
else if (info.flags & T_CAN_SUPPORT_MDATA) |
result = true; // this also means that the src addr info is in the info record |
else |
result = false; |
return result; |
} |
void DoOTLLCWriteTest(void) |
{ |
OSStatus osstatus; |
UInt32 numMemErrs, last250; |
UInt32 lastFlowErrPacketNum; |
UInt32 timer; |
time_t t1, t2, t3; |
UInt16 rawModeOffset = 0; |
Boolean callDoIdle; |
osstatus = DoBind(); |
OTMemzero(&gPacket, sizeof(gPacket)); // zero out the global packet buffer structure |
if (TstUseAckSendsFlag(gFlags)) |
{ |
osstatus = OTAckSends(gEndpoint); |
if (osstatus != kOTNoError) |
{ |
fprintf (stdout, "\n Error turning AckSends on - error %ld", osstatus); |
ClrUseAckSendsFlag(gFlags); |
gNumFore = gNumBack = 0; |
} |
} |
if (TstUseRawModeFlag(gFlags)) |
{ |
if (CanDoMDATAMode(gEndpoint) && (DATASIZE <= 1500)) |
{ |
osstatus = DoNegotiateRawModeOption(gEndpoint, kOTRawRcvOn, &gTemplateType); |
if (osstatus == kOTNoError) |
{ |
SetRawModeFlag(gFlags); |
/* if rawmode is on then we want to offset the data |
* an additional 17 bytes and set the header info |
* ourselves. |
* note that 17 bytes constitutes the 6 byte dAddr, 6 byte sAddr |
* 2 byte length field, 1 byte ssap, 1 byte dsap and 1 byte control byte |
* Note that even if the DSAP is not 0xAA, we go ahead and stuff the 5 bytes |
* following the control byte, with whatever SNAP values have been defined. |
*/ |
gPacket.rawModeOffset = 17; |
// check is we are doing SNAP |
if (TESTSAP == 0xAA) |
gPacket.rawModeOffset += 5; |
fprintf (stdout, "\n raw mode option enabled"); |
} |
else |
{ |
// if the option failed, then we don't use it' |
fprintf (stdout, "\nError negotiating raw mode option"); |
// reset the status result. |
osstatus = kOTNoError; |
} |
} |
else |
fprintf (stdout, "\nYou need a later version of OT which supports the MDataMode option"); |
} |
#if __ASYNCSEND__ |
if (osstatus == kOTNoError) |
{ |
osstatus = OTSetAsynchronous(gEndpoint); |
if (osstatus != kOTNoError) |
{ |
fprintf(stderr, "\n\nError making endpoint asynchronous!"); |
fprintf(stderr, "\nOTSetAsynchronous returned %d\n", osstatus); |
} |
} // now ready to handle async events |
#endif |
if (osstatus == kOTNoError) |
{ |
// set up the first 18 bytes past the control byte for non SNAP LLC endpoint or |
// past the SNAP header for a SNAP endpoint, so that we can recognize it |
OTStrCopy((char*)&gPacket.data[gPacket.rawModeOffset], "begin data section"); |
// set up some specific bytes in the data buffer that begins at the same point |
// relative to the LLC or SNAP header |
gPacket.data[DATAOFFSET + gPacket.rawModeOffset] = 0; |
gPacket.data[DATAOFFSET + 1 + gPacket.rawModeOffset] = 0; |
OTStrCopy((char*)&gPacket.data[DATAOFFSET+2 + gPacket.rawModeOffset], "end of data section"); |
gPacket.unitdata.udata.buf = (UInt8*)gPacket.data; |
if (TstRawModeFlag(gFlags)) |
gPacket.unitdata.udata.len = DATASIZE + 14; |
else |
{ |
// the DATASIZE setting represents the total size of the packet |
// following the ethernet header. If we are not doing a rawmode send |
// then we need to account for the fact that part of the data area |
// will be used by OT to place the SSAP, DSAP, and Control byte. If |
// we are sending a SNAP packet, then we have to account for the 5 |
// additional bytes of the SNAP header. Otherwise if the unidata.data.len |
// field were set to the full size of 1500, the packet would not be sent |
// since OT would think it was trying to send a 1503 or 1508 byte |
// ethernet packet (not including the ethernet header). |
// By doing this, we can process the packet using either a raw data or |
// regular ethernet endpoint as the receiver. |
if (TESTSAP == 0xAA) |
gPacket.unitdata.udata.len = DATASIZE - 8; |
else |
gPacket.unitdata.udata.len = DATASIZE - 3; |
} |
gPacket.unitdata.opt.len = 0; |
gPacket.unitdata.opt.buf = NULL; |
if (TstRawModeFlag(gFlags) == false) |
{ |
// set up the destination addresss |
gPacket.dAddr.fAddrFamily = AF_8022; |
gPacket.dAddr.fHWAddr[0] = MCASTADDR0; |
gPacket.dAddr.fHWAddr[1] = MCASTADDR1; |
gPacket.dAddr.fHWAddr[2] = MCASTADDR2; |
gPacket.dAddr.fHWAddr[3] = MCASTADDR3; |
gPacket.dAddr.fHWAddr[4] = MCASTADDR4; |
gPacket.dAddr.fHWAddr[5] = MCASTADDR5; |
gPacket.dAddr.fSAP = TESTSAP; |
gPacket.unitdata.addr.buf = (UInt8*)&gPacket.dAddr; |
if (TESTSAP == 0xAA) |
{ |
gPacket.dAddr.fSNAP[0] = MYSNAP0; |
gPacket.dAddr.fSNAP[1] = MYSNAP1; |
gPacket.dAddr.fSNAP[2] = MYSNAP2; |
gPacket.dAddr.fSNAP[3] = MYSNAP3; |
gPacket.dAddr.fSNAP[4] = MYSNAP4; |
gPacket.unitdata.addr.len = k8022SNAPAddressLength; |
} |
else |
gPacket.unitdata.addr.len = k8022BasicAddressLength; |
} |
else |
{ |
// set up for a rawmode send data call |
gPacket.data[0] = MCASTADDR0; |
gPacket.data[1] = MCASTADDR1; |
gPacket.data[2] = MCASTADDR2; |
gPacket.data[3] = MCASTADDR3; |
gPacket.data[4] = MCASTADDR4; |
gPacket.data[5] = MCASTADDR5; |
// set the packet len field |
gPacket.data[12] = DATASIZE >> 8; |
gPacket.data[13] = DATASIZE & 0xFF; |
// set the dsap, ssap, and control byte fields. |
gPacket.data[14] = TESTSAP; // set DSAP |
gPacket.data[15] = TESTSAP; // set SSAP |
gPacket.data[16] = 0x03; // set control byte |
if (TESTSAP == 0xAA) |
{ |
// set up the SNAP Addr |
gPacket.data[17] = MYSNAP0; |
gPacket.data[18] = MYSNAP1; |
gPacket.data[19] = MYSNAP2; |
gPacket.data[20] = MYSNAP3; |
gPacket.data[21] = MYSNAP4; |
} |
gPacket.unitdata.addr.buf = nil; |
// don't want to set the destination address since we've already |
// done so in the data |
// the following line is required for OT 1.2 and greater |
// where there is a bug with how OT deals with mentat template |
// based drivers, such that in rawmode, the total packet size - |
// header + data is limited to 1500 bytes. By using the following |
// line, OT will not check for this limitation and will go ahead |
// and send an MDATA message. |
gPacket.unitdata.addr.len = 0xFFFFFFFFL; |
// magic constant for M_DATA mode |
} |
ClrFlowClrFlag(gFlags); // clear the flag that indicates that a T_GODATA event occurred |
// we do this because a race condition might occur when we make the |
// the OTSndUData call, a flowerr may occur, but get cleared by the time |
// we actually check the osstatus field |
fprintf (stdout, "\n starting write of %ld llc packets of %ld bytes\n", (long)SENDCOUNT, (long)DATASIZE); |
fflush(stdout); |
t1 = clock (); |
gDone = false; |
gPacket.i = 0; |
numMemErrs = last250 = 0; |
lastFlowErrPacketNum = 0; |
timer = 0; |
callDoIdle = false; |
// set the flag that will tell us to send a packet at system task time so that |
// we can send the initial packet |
SetSysTaskSendFlag(gFlags); |
while (!gDone && !gAbort) |
{ |
if (TstSysTaskSendFlag(gFlags)) |
{ |
if (DoSendPacket(gEndpoint) == true) |
{ |
gNumFore++; |
} |
} |
if (gNumMemErrs) |
{ |
if (numMemErrs != gNumMemErrs) |
{ |
numMemErrs = gNumMemErrs; |
fprintf(stderr, "\nkENOMEMErr %ld.", gPacket.i); |
callDoIdle = true; |
} |
} |
#if 0 // the following code really affect the performance of this tool |
// and can consume over 95 percent of the processing time of this tool |
// on a fast system. |
n = gPacket.i / 250; |
if (n > last250) |
{ |
fprintf(stderr, "%d ", n * 250); |
fflush(stderr); |
last250 = n; |
} |
#endif |
timer++; |
if (timer > gTimerThreshold) |
{ |
callDoIdle = true; |
} |
if (gPacket.lastFlowErrPacketNum != 0) |
{ |
if (lastFlowErrPacketNum != gPacket.lastFlowErrPacketNum) |
{ |
fprintf(stderr, "\nflow error occurred while sending packet %d.", gPacket.lastFlowErrPacketNum); |
lastFlowErrPacketNum = gPacket.lastFlowErrPacketNum; |
} |
} |
// check if we are flow controlled or if it's time to call WNE and call it. |
if ((callDoIdle == true) || TstFlowErrFlag(gFlags)) |
{ |
CallWNE(); |
callDoIdle = false; |
timer = 0; |
} |
} // end while loop sending data |
fflush(stderr); |
t2 = clock(); |
t3 = t2 - t1; |
fprintf (stdout, "\nCompleted sending %ld llc packets.", (long)gPacket.i); |
fprintf (stdout, "\nTime start = %ld, time end = %ld, time taken %ld seconds.", |
t1, t2, (long)t3/CLOCKS_PER_SEC); |
fprintf (stdout, "\nPackets per second = %ld, bytes/second = %ld.", |
((long)gPacket.i*CLOCKS_PER_SEC)/t3, ((long)gPacket.i*DATASIZE*CLOCKS_PER_SEC)/t3); |
fprintf (stdout, "\n Press the mouse to unbind the endpoint"); |
if (TstUseAckSendsFlag(gFlags) == true) |
{ |
fprintf (stdout, "\nThe number of packets sent from the while loop - %ld", gNumFore); |
fprintf (stdout, "\nThe number of packets sent from the handler - %ld", gNumBack); |
} |
fflush(stdout); |
while (!Button()) |
MyIdle(); |
} // end if bind successful |
#if __ASYNCSEND__ |
if (TstEPBoundFlag(gFlags)) |
{ |
OTSetSynchronous(gEndpoint); |
OTUnbind(gEndpoint); |
} |
#endif |
} |
Boolean DoSendPacket(EndpointRef ep) |
{ |
OSStatus osstatus; |
Boolean didEnter; |
// check to see if we have already entered this function. |
// we only want to enter into it once whether at system task time or from the |
// handler |
if (OTAtomicSetBit(&gFlag1, kInSendPacketBit)) |
return false; |
// call OTenterNotifier so that the notifier is not entered while we are in the following |
// section of code. In this way we protect ourselves from race conditions that could |
// happen - for example if a flowErr occured, but was cleared before we set the flowErr |
// flag, this would be a problem since we would never receive notification that flow |
// control was lifted. |
didEnter = OTEnterNotifier(ep); |
if (gDone == false) |
{ |
// we already assume that the packet is ready to send |
osstatus = OTSndUData(gEndpoint, &gPacket.unitdata); |
} |
switch (osstatus) |
{ |
case kENOMEMErr: |
gNumMemErrs++; |
break; |
case kOTNoError: // send was successful |
gPacket.i++; |
if (gPacket.i >= SENDCOUNT) |
gDone = true; |
else |
{ |
// increment the counter in the packet |
gPacket.data[DATAOFFSET+0+gPacket.rawModeOffset] = gPacket.i >> 8; |
gPacket.data[DATAOFFSET+1+gPacket.rawModeOffset] = gPacket.i; |
} |
break; |
case kOTFlowErr: |
SetFlowErrFlag(gFlags); // a T_GODATA did not just come in |
ClrSysTaskSendFlag(gFlags); // clear the flag that we check to send a packet |
gPacket.lastFlowErrPacketNum = gPacket.i; // save the packet number on which the flow err occured |
break; |
default: |
DoValueBreak(osstatus, "Unknown error occurred in DoSendPacket #;"); |
gDone = true; |
ClrSysTaskSendFlag(gFlags); |
break; |
} |
if (didEnter == true) |
OTLeaveNotifier(ep); |
OTAtomicClearBit(&gFlag1, kInSendPacketBit); |
return true; |
} |
void DoOTLLCReadTest(void) |
{ |
OSStatus osstatus; |
long timer; |
gBuffer = (UInt8*)NewPtr(DATASIZE + 2* DATASLOP); // allocate the data buffer + some slop |
if (gBuffer) |
osstatus = DoBind(); |
else |
{ |
osstatus = memFullErr; |
return; |
} |
gBackToBackPackets = 0; |
gInOrder = 0; |
gOutOfOrder = 0; |
gCounter = 0; |
gPacketsRead = 0; |
gNumDataEvents = 0; |
gReadErrors = 0; |
gNumMemErrs = 0; |
gDone = false; |
if (osstatus == kOTNoError) |
{ |
osstatus = DoAddMulticast(gEndpoint, gmcAddr); |
} |
if (osstatus == kOTNoError) |
{ |
if (TstUseRawModeFlag(gFlags)) |
{ |
osstatus = DoNegotiateRawModeOption(gEndpoint, kOTRawRcvOn, &gTemplateType); |
if (osstatus == kOTNoError) |
{ |
SetRawModeFlag(gFlags); |
} |
else |
{ |
// if the option failed, then we don't use it' |
fprintf (stdout, "\nError negotiating raw mode option"); |
// reset the status result. |
osstatus = kOTNoError; |
} |
} |
} |
#ifdef __ASYNCSEND__ |
if (osstatus == kOTNoError) |
{ |
osstatus = OTSetAsynchronous(gEndpoint); |
if (osstatus != kOTNoError) |
{ |
fprintf(stderr, "\n\nError making endpoint asynchronous!"); |
fprintf(stderr, "\nOTSetAsynchronous returned %d\n", osstatus); |
} |
} // now ready to handle async events |
#endif |
if (osstatus == kOTNoError) |
{ |
gAbort = false; |
SetWantDataFlag(gFlags); |
fprintf (stdout, "\nStarting Read test - will terminate in %d seconds", TIMEOUT); |
fprintf (stdout, "\nor as soon as the trigger packet is received."); |
fprintf (stdout, "\nYou may use Command-Q to quit the program entirely."); |
fprintf (stdout, "\nYou may also use Command-A to terminate this test.\n"); |
fprintf (stdout, "\nStarting Read"); |
fflush(stdout); |
timer = TickCount() + TIMEOUT * 60; |
// loop until timer is less than TickCount or until the endtime value gets set |
while ((timer > TickCount()) && (gDone == false) && (gAbort == false)) |
{ |
// allow the user to exit the timer loop by |
CallWNE(); |
} |
} |
OTSetSynchronous(gEndpoint); |
if (TstMCastActiveFlag(gFlags)) |
DoRemoveMulticast(gEndpoint, gmcAddr); |
if (osstatus == kOTNoError) |
{ |
fprintf (stdout, "\n\nBufferReadCount = %ld", gPacketsRead); |
fprintf (stdout, "\nInOrder = %ld\n", gInOrder); |
fprintf (stdout, "\nOutofOrder = %ld\n", gOutOfOrder); |
fprintf (stdout, "\nlast packet read was = %ld\n", gCounter); |
fprintf (stdout, "\nNumber of data events was = %ld\n", gNumDataEvents); |
fprintf (stdout, "\nNumber of read errors was = %ld\n", gReadErrors); |
fprintf (stdout, "\nNumber of back to back packets was = %ld\n", gBackToBackPackets); |
fflush(stdout); |
} |
if (TstEPBoundFlag(gFlags)) |
OTUnbind(gEndpoint); |
if (gBuffer) |
DisposePtr((Ptr)gBuffer); |
} |
OSStatus DoReadPacket(EndpointRef ep, UInt8 *mainBuffer) |
{ |
TUnitData unitdata; |
struct T8022Address dAddr; |
OTFlags otFlags; |
OSStatus result; |
unitdata.addr.maxlen = sizeof(dAddr); |
unitdata.addr.buf = (UInt8*)&dAddr; |
unitdata.udata.buf = mainBuffer; |
unitdata.udata.maxlen = DATASIZE + DATASLOP; |
unitdata.opt.maxlen = 0; |
result = OTRcvUData(ep, &unitdata, &otFlags); |
return result; |
} |
pascal void LLCEventHandler(void* ref, OTEventCode event, OTResult result, void* cookie) |
{ |
#pragma unused(ref,cookie) |
OSStatus osstatus; |
UInt8 *bufferToUse; |
UInt32 lcounter, offset; |
Boolean firstTimeFlag; |
gstatus = result; |
switch (event) |
{ |
case T_MEMORYRELEASED: |
if (DoSendPacket(gEndpoint) == false) |
SetSysTaskSendFlag(gFlags); |
else |
{ |
ClrSysTaskSendFlag(gFlags); |
gNumBack++; |
} |
break; |
case T_OPTMGMTCOMPLETE: |
ClrWaitOptMgmtFlag(gFlags); |
break; |
case T_BINDCOMPLETE: |
ClrStillBindFlag(gFlags); |
break; |
case T_UNBINDCOMPLETE: |
break; |
case T_GODATA: |
SetFlowClrFlag(gFlags); // indicate that the flow data problem has now cleared. |
SetSysTaskSendFlag(gFlags); |
break; |
case T_DATA: |
gNumDataEvents++; |
if (TstWantDataFlag(gFlags)) |
bufferToUse = gBuffer; |
else |
bufferToUse = gDummyBuffer; |
// initialize variables as we enter while loop |
osstatus = kOTNoError; |
firstTimeFlag = true; |
while ((osstatus == kOTNoError) && (!gDone)) |
{ |
osstatus = DoReadPacket(gEndpoint, bufferToUse); |
if (firstTimeFlag == true) |
// this is the first time through this loop |
// for this call to the handler |
firstTimeFlag = false; |
else |
// increment the counter to indicate that there was a packet to |
// handle after reading the previous packet |
gBackToBackPackets++; |
if (osstatus != kOTNoDataErr) |
{ |
if (osstatus < 0) |
gReadErrors++; |
else |
gPacketsRead++; |
} |
if (TstWantDataFlag(gFlags) && (osstatus == kOTNoError)) |
{ |
if (TstRawModeFlag(gFlags)) |
{ |
// if rawmode is on then we want to account for |
// the additional 17 bytes which will be at the |
// beginning of the packet. |
offset = 17; |
if (TESTSAP == 0xAA) |
{ |
offset += 5; // account for the SNAP header |
} |
// check if the template is a mentat template and |
// adjust the offset to account for the additional |
// bytes that the template prepends to the ethernet |
// packet. |
if (gTemplateType == kMentatTemplate) |
offset += sizeof(dl_recv_status_t); |
} |
else |
offset = 0; |
lcounter = gBuffer[DATAOFFSET + offset +0] << 8; |
lcounter |= gBuffer[DATAOFFSET + offset +1]; |
if (lcounter >= TRIGGEREND) |
{ |
gDone = true; // we can bail now. |
} |
else |
{ |
if (lcounter == gCounter) |
gInOrder++; |
else |
gOutOfOrder++; |
gCounter = lcounter + 1; // prepare gCounter for next incoming packet to compare |
} |
} |
} |
break; |
default: |
DoValueBreak(event, "Unknown event occurred # ;g"); |
break; |
} /* end switch on event */ |
} |
/******************************************************************************* |
** CallWNE is implemented to call WaitNextEvent to check for the Command-Q key |
** sequence. When this happens, then the gdone flag is set to true |
********************************************************************************/ |
void CallWNE(void) |
{ |
EventRecord event; |
char key; |
if (!WaitNextEvent(everyEvent, &event, 15, nil)) |
event.what = nullEvent; |
switch (event.what) |
{ |
case nullEvent: |
case mouseDown: |
case activateEvt: |
case updateEvt: |
case kHighLevelEvent: |
case osEvt: |
case diskEvt: |
break; |
case autoKey: |
case keyDown: |
key = event.message & charCodeMask; |
switch (key) |
{ |
case 'q': |
case 'Q': |
if (event.modifiers & cmdKey) |
gDone = true; |
break; |
case 'a': |
case 'A': |
if (event.modifiers & cmdKey) |
gAbort = true; |
break; |
} |
break; |
} |
} |
void MyIdle(void) |
{ |
EventRecord event; |
OSErr err; |
err = WaitNextEvent(everyEvent, &event, 15 + gNumMemErrs * 5, nil); |
} |
void main (void) |
{ |
OSStatus osstatus = noErr; |
UInt32 selection; |
char drvrname[64]; |
Boolean done = false; |
WriteApplIntro(); |
gFlags = 0; |
if (osstatus = InitOpenTransport()) |
{ |
fprintf(stderr, "\n\nOpen Transport is not installed!\n"); |
fprintf(stderr, "\nBye Bye.\n"); |
} |
else |
{ |
SetOTActiveFlag(gFlags); // indicate that OT is active |
gDummyBuffer = (UInt8*)NewPtr(DATASIZE + 2 * DATASLOP); |
if (gDummyBuffer == NULL) |
osstatus = memFullErr; |
} |
PrintAppleTalkPortName(); |
while (done == false) |
{ |
osstatus = OTSetMemoryLimits(10240000, 0); |
if (osstatus != kOTNoError) |
{ |
fprintf(stderr, "\n OTSetMemoryLimits returned %d\n", osstatus); |
osstatus = kOTNoError; |
} |
if (osstatus != kOTNoError) |
{ |
done = true; |
break; |
} |
ListEnetDrivers(); |
GetDriverName(drvrname); |
// open the default ethernet endpoint |
gEndpoint = OTOpenEndpoint(OTCreateConfiguration(drvrname), (OTOpenFlags)NULL, NULL, &osstatus); |
if (osstatus != kOTNoError) |
{ |
fprintf(stderr, "\n\nError opening Ethernet endpoint!"); |
fprintf(stderr, "\nOTOpenEndpoint returned %d\n", osstatus); |
fprintf(stderr, "\nBye Bye.\n"); |
done = true; |
} |
else |
SetEPActiveFlag(gFlags); // indicate that the endpoint is opened |
if (osstatus == kOTNoError) |
{ |
osstatus = OTInstallNotifier(gEndpoint, LLCEventHandler, NULL); |
if (osstatus != kOTNoError) |
{ |
fprintf(stderr, "\n\nError installing notifier!"); |
fprintf(stderr, "\nOTInstallNotifier returned %d\n", osstatus); |
} |
} // now ready to handle async events |
if (osstatus == kOTNoError) |
{ |
// ask whether to use the rawmode option or not |
fprintf(stdout, "\nDo you want to use the raw mode option?"); |
selection = GetYesNoOption(); |
if (selection == kQuitTest) |
{ |
fprintf(stderr, "\n\nBye-Bye!"); |
osstatus = -1; |
done = true; |
} |
else if (selection == kAcceptOption) |
SetUseRawModeFlag(gFlags); |
else if (selection == kDeclineOption) |
ClrUseRawModeFlag(gFlags); |
} |
if (osstatus == kOTNoError) |
{ |
// what does the user want to do |
selection = GetUserOption(); |
switch (selection) |
{ |
case kSendTest: |
fprintf(stdout, "\nDo you want to turn on AckSends?"); |
selection = GetYesNoOption(); |
if (selection == kQuitTest) |
{ |
fprintf(stderr, "\n\nBye-Bye!"); |
done = true; |
break; |
} |
else if (selection == kAcceptOption) |
{ |
SetUseAckSendsFlag(gFlags); |
} |
else if (selection == kDeclineOption) |
{ |
ClrUseAckSendsFlag(gFlags); |
SetTimerThreshold(); |
} |
// pause until the user clicks the mouse button |
fprintf(stderr, "Click Mouse to start test\n"); |
while (!Button()); |
DoOTLLCWriteTest(); |
break; |
case kReceiveTest: |
// pause until the user clicks the mouse button |
fprintf(stderr, "\n\nClick Mouse to start test\n"); |
while (!Button()); |
DoOTLLCReadTest(); |
break; |
case kQuitTest: |
default: |
fprintf(stderr, "\n\nBye-Bye!"); |
done = true; |
break; |
} |
} |
if (TstEPActiveFlag(gFlags)) |
{ |
// force endpoint to be synchronous |
OTSetSynchronous(gEndpoint); |
OTCloseProvider(gEndpoint); |
} |
if (done == false) |
{ |
fprintf(stdout, "\n\nDo you want to repeat the test?"); |
selection = GetYesNoOption(); |
if (selection != kAcceptOption) |
done = true; |
} |
} |
if (gDummyBuffer) |
DisposePtr((Ptr)gDummyBuffer); |
OTSetMemoryLimits(0, 0); |
if (TstOTActiveFlag(gFlags)) |
CloseOpenTransport(); |
fprintf(stderr, "\nProgram ended"); |
} |
void DoValueBreak(long value, const char* message) |
{ |
static short sDoErrorBreak = 0; |
{ |
Str255 s, |
n = "\p"; |
s[0] = strlen(message); |
BlockMoveData(message,&s[1],s[0]); |
if (value < 0) |
{ |
s[0] += 1; |
s[s[0]] = '-'; |
value = -value; |
} |
while (value) |
{ |
if (n[0]) |
BlockMoveData(&n[1],&n[2],n[0]); |
n[0]++; |
n[1] = 48 + (value % 10); |
value /= 10; |
} |
BlockMoveData(&n[1],&s[s[0]+1],n[0]); |
s[0] += n[0]; |
sDoErrorBreak++; |
{ |
short cnt = sDoErrorBreak; |
s[0]++; |
s[s[0]] = ','; |
s[0]++; |
s[s[0]] = ' '; |
n[0] = 0; |
while (cnt) |
{ |
if (n[0]) |
BlockMoveData(&n[1],&n[2],n[0]); |
n[0]++; |
n[1] = 48 + (cnt % 10); |
cnt /= 10; |
} |
BlockMoveData(&n[1],&s[s[0]+1],n[0]); |
s[0] += n[0]; |
} |
DebugStr(s); |
} |
} |
void GetDriverName(char *name) |
{ |
fprintf(stdout, "\nEnter the driver name to use"); |
fprintf(stdout, "\nEnter an invalid driver name to quit"); |
fprintf(stdout, "\n : "); |
while (true) |
{ |
gets(name); |
if (*name != 0) |
break; |
} |
} |
void SetTimerThreshold(void) |
{ |
char chr, str[256]; |
size_t len, i; |
Boolean done = false; |
while (done == false) |
{ |
fprintf(stdout, "\n Enter the timer threshold"); |
fprintf(stdout, "\n This is the number of times that the write loop loops before calling"); |
fprintf(stdout, "\n WaitNextEvent. Enter a value of 10000 to never call WNE."); |
fprintf(stdout, "\n Enter a value of 100 to call WNE every 100 packets.\n"); |
while (true) |
{ |
gets(str); |
if (*str != 0) |
break; |
} |
gTimerThreshold = 0; |
len = strlen(str); |
for (i = 0; i < len; i++) |
{ |
chr = str[i]; |
if ((chr >= '0') && (chr <= '9')) |
gTimerThreshold = gTimerThreshold*10 + chr - '0'; |
} |
if (gTimerThreshold != 0) |
{ |
done = true; |
fprintf(stdout, "\n The timer threshold is set at %ld\n\n", gTimerThreshold); |
fflush(stdout); |
} |
} |
} |
/* |
The following routine prints the name of the port currently being used by AppleTalk |
If AppleTalk is using Ethernet, then the fPortName field could be used in the |
OpenEndpoint call to open an ethernet endpoint on the same hardware port as AppleTalk |
*/ |
void PrintAppleTalkPortName(void) |
{ |
ATSvcRef atref; |
OSStatus err; |
OTPortRef portref; |
OTPortRecord portrecord; |
TEndpointInfo info; |
atref = OTOpenEndpoint(OTCreateConfiguration(kDDPName), 0, &info, &err); |
if (err == kOTNoError) |
{ |
portref = OTGetProviderPortRef(atref); |
if (portref != nil) |
{ |
if (OTFindPortByRef(&portrecord, portref) == true) |
{ |
// fprintf(stderr, "\n Appletalk port name is %s", portrecord.fPortName); |
portref = portrecord.fChildPorts[0]; |
if (OTFindPortByRef(&portrecord, portref) == true) |
fprintf(stdout, "\n Appletalk child port name is %s", portrecord.fPortName); |
else |
fprintf(stdout, "\n Appletalk child port record could not be found using PortRef %lX", portref); |
} |
else |
fprintf(stdout, "\n Appletalk port record could not be found using PortRef %lX", portref); |
} |
else |
fprintf(stdout, "\n OTGetProviderPortRef returned nil result"); |
OTCloseProvider(atref); |
} |
else |
fprintf(stdout, "\n OTOpenAppleTalkServices returned error %d", err); |
} |
void ListEnetDrivers(void) |
{ |
OTPortRecord portRecord; |
Boolean foundAPort; |
UInt32 index; |
Str255 userFriendlyName; |
fprintf(stdout, "\n The list of drivers present is:\n"); |
index = 0; |
// iterate thru each OT port record for ethernet ports. |
while (foundAPort = OTGetIndexedPort(&portRecord,index)) |
{ |
if ((portRecord.fCapabilities & kOTPortIsDLPI) && |
(portRecord.fCapabilities & kOTPortIsTPI) && |
(kOTEthernetDevice == OTGetDeviceTypeFromPortRef(portRecord.fRef))) |
{ |
OTGetUserPortNameFromPortRef(portRecord.fRef, userFriendlyName); |
fprintf(stdout, "\n Driver name - %s, ", portRecord.fPortName); |
fprintf(stdout, "user readable name is - %#s", userFriendlyName); |
} |
index++; |
} |
fprintf(stdout, "\n"); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22