SLI Practical Group B Tail-Wag

PPP

Code

You can download the VCC library for PPP as a zip file here.

The WhiteBox C code for the PPP Controller model is presented below. It's probably rather incomprehensible out of context. It is advisable to download the entire library and import it into a copy of VCC for a better view, including a view of the state-machine.


#include "white_interface.h"
#define abort() 1

#define DEBUG 0

#define debugPrint(x) if(DEBUG) vccPrintPdxDebugInfo x

extern int vccPrintPdxDebugInfo(const char *format, ...);
static int rfcommCts;
static int rfcommConnected;
static Packet lastReceivedPacket;
static char lastConfigureReqIdentifier;
static int connectionEstablished;
static int initiator;

static int servDlc;
static int myDlci;

ServiceRequest* toSend;

static char* construct_LCP_Packet(char code, char identifier, int length, Packet* packet);
static void receivedAction(Actions action);
static void makeServiceRequest(Services service, Commands command, char* data, int size);
static void receivedServiceRequest(ServiceRequest* requestIn);
static int ConfigurationAcceptable(Packet* packet);

typedef struct lcp_packet_mask {
  char lcpCode;
  char lcpIdentifier;
  unsigned short lcpLength;
  char data;
} lcp_packet_mask;

enum codes {
    configureRequest=90, 
	configureAck, 
	configureNak, 
	configureReject, 
	terminateRequest,
	terminateAck, 
	codeReject, 
	protocolReject, 
	echoRequest, 
	echoReply, 
	discardRequest
};

/* Init Function */
void poin_entry_Init() {
	servDlc = ServerDlc_Value();
	rfcommCts=1;
	toSend=0;
	rfcommConnected=0;
	myDlci = 0;
	if(RFCOMMMaster_Value()) {
		makeServiceRequest(START, request, 0, 0);
		initiator = 1;
	} else {
	    initiator = 0;
	}
}  

int ConfigurationAcceptable(Packet* packet) {
	static int times=0;
    lcp_packet_mask* msk = (lcp_packet_mask*)packet->Data;
	/* We only support the default options, which means that our congifuration
	   packets are always of zero length.  If we reject packets which are not 
       of zero-length, the party we're negotiating will eventually /have/ to
	   agree on the defaults with us.  It's not very 'friendly' to the other
	   party, but since we're only ever gonig to support the dafualts
	   anyway, it seems inefficient to do anything more complex. */ 

	debugPrint(("%_START_ ***** Configuration packet of length %d received ***** %_END_", msk->lcpLength));

	/* In debug builds, we reject the first configuration packet all the time
	   as a test to see if configuration will negotiate correctly. */
	if(DEBUG)
		return(times++ && msk->lcpLength==0);
	else
		return(msk->lcpLength==0);
}

/* Run Function */
void poin_entry_Run() {
	if(Action_Enabled()) {
		receivedAction(Action_Value());
	}
	if(RFCOMMServiceRequestIn_Enabled()) {
		receivedServiceRequest(RFCOMMServiceRequestIn_Value());
	}
	if(PacketIn_Enabled()) {
		makeServiceRequest(DATA, request, (char*)PacketIn_Value(), sizeof(Packet));
	}
	if(ConnectTo_Enabled()) {
	    int val = ConnectTo_Value();
		if(val) {
			myDlci = (val << 1 | initiator);
			Event_Post(Open);
		} else {
			Event_Post(Close);
		}
	}
	if(RFCOMMConnected_Enabled()) {
		if(RFCOMMConnected_Value()) {
		    /* If we're initiating the PPP connection, we need to establish 
			   the RFCOMM DLC */
			if(initiator)
				makeServiceRequest(DLC_ESTABLISHMENT, request, 0, 0);
		} else {	
			Event_Post(Down);
		}
	}
	if(RFCOMMClear_Enabled()) {
		if(RFCOMMClear_Value()) {
			rfcommCts=1;
			if(toSend) {
				/* If we've gut buffered data, send it... */
				rfcommCts=1;
				RFCOMMServiceRequestOut_Post(toSend);
				toSend=0;
			} else {
				/* ...otherwise, raise our clear-to-send flag. */
				ClearToSend_Post(1);
			}
		} else {
			rfcommCts=0;
		}
	}
}

static void receivedServiceRequest(ServiceRequest* requestIn) {
	memcpy(&lastReceivedPacket, requestIn, sizeof(Packet));

	if((requestIn->Service == DLC_ESTABLISHMENT) )
	{
		if(requestIn->Command == indication && ( *((unsigned short*)&(requestIn->Data)) == (unsigned short)(servDlc << 1 | !initiator))) {
			/* For now, accept all establishment requests*/	
			myDlci = *((unsigned short*)&(requestIn->Data));
			makeServiceRequest(DLC_ESTABLISHMENT, response, ((char*)requestIn->Data)+2, sizeof(requestIn->Data)-2);
			Event_Post(Open);
			Event_Post(Up);
			rfcommConnected=1;
			debugPrint(("%_START_ Accepting establishment request. DLCI is now %d %_END_", myDlci));
		} else if (requestIn->Command == confirm && *((unsigned short*)&(requestIn->Data)) == myDlci) {
			/* We've connected */
			Event_Post(Up);
			rfcommConnected=1;
			debugPrint(("%_START_ Connection Established. %_END_"));
		}
	} else if (	*((unsigned short*)&(requestIn->Data)) == myDlci) {
		switch(requestIn->Service) {
	
		case DLC_RELEASE:
			/* The link has been closed */
			if(requestIn->Command == indication) {	
		   		ConnectionEstablished_Post(0);	
				connectionEstablished=0;
				Event_Post(Down);					 
				break;
			}
			break;
		case DATA:
			if(requestIn->Command == indication) {
				/* We've received a PPP packet */
				Packet* pkt = (Packet*)(((char*)&(requestIn->Data))+2);
				switch(pkt->Protocol) {
				case 0xc021:
					/* It's an LCP packet */
					switch(((char*)(pkt->Data))[0]) {
					case configureRequest:
						if(ConfigurationAcceptable(pkt)) {
							Event_Post(RCRplus);
						} else {
							Event_Post(RCRminus);
						}
						break;
					case configureAck:
						Event_Post(RCA);
						break;
					case configureNak: 
					case configureReject: 
						Event_Post(RCN);
						break;
					case terminateRequest:
						Event_Post(RTR);
						break;
					case terminateAck:
						Event_Post(RTA);
						break;
					case codeReject:
					case protocolReject: 
						/* We should look at what's been rejected here to decide
						   whether to send an RXJplus or an RXJminus */
						Event_Post(RXJplus);
						break;
					case echoRequest:
					case echoReply:
					case discardRequest: 
						Event_Post(RXR);
						break;
					}
					break;
				default:
					/* We'll pass packets we don't know about to the next layer up */
					PacketOut_Post(pkt);
					break;
				}
			}
		}
		/* We should never get here! */
		abort();
	}
}

static void receivedAction(Actions action) {
	Packet my_packet;
	int identifier = 0;
	
	/* The automaton wants us to do something */
	switch(action) {
	case tlu:
		/* Signal to layers above us that the link is up */
		ConnectionEstablished_Post(2);
		connectionEstablished=2;
		break;
	case tld:
		/* Signal to layers above us that the link is down */
   		ConnectionEstablished_Post(1);	
		connectionEstablished=1;
		break;
	case tls:
		/* Send to RFCOMM that we want to start.
		   It should respond.  When it does, we generate an Up event. 
		   [see receivedServiceRequest] */
		break;
	case tlf:
		/* Send to RFCOMM that we want to stop.
		   (It should respond.  When it does, we generate an Down event.
		   [see receivedServiceRequest] */
		makeServiceRequest(DLC_RELEASE, request, 0, 0);
		if(connectionEstablished!=0) {
			ConnectionEstablished_Post(0);	
			connectionEstablished=0;
		}
		Event_Post(Down);
		break;
	case irc:
		/* Set the restart counter to Max-Terminate or Max-Request */
		break;
	case zrc:
		/* Zero the restart counter */
		break;
	case scr:
		/* Transmit a configure-request packet. */
		construct_LCP_Packet(configureRequest, ++identifier, 0, &my_packet);
		makeServiceRequest(DATA, request, (char*)&my_packet, sizeof(Packet));
		/* Start the restart timer. */
		break;
	case sca:
		/* Send a configure-ack packet */
		construct_LCP_Packet(configureAck, ++identifier, 0, &my_packet);
		makeServiceRequest(DATA, request, (char*)&my_packet, sizeof(Packet));
		break;
	case scn:
		/* Send a configure-nak or configure-reject packet (because an 
		   unacceptable configuration request was received). */	  	
		construct_LCP_Packet(configureReject, ++identifier, 0, &my_packet);
		makeServiceRequest(DATA, request, (char*)&my_packet, sizeof(Packet));
		break;
	case str:
		/* Send a terminate-request packet and decrement the restart counter */
		construct_LCP_Packet(terminateRequest, ++identifier, 0, &my_packet);
		makeServiceRequest(DATA, request, (char*)&my_packet, sizeof(Packet));
		break;
	case sta:
		/* Send a Terminate-Ack packet. */
		construct_LCP_Packet(terminateAck, ++identifier, 0, &my_packet);
		makeServiceRequest(DATA, request, (char*)&my_packet, sizeof(Packet));
		break;
	case scj:
		/* Send a code-reject packet (we've received an unknown type of packet) 
		   This should never happen. */
		construct_LCP_Packet(codeReject, ++identifier, 0, &my_packet);
		makeServiceRequest(DATA, request, (char*)&my_packet, sizeof(Packet));
  
		vccPrintPdxDebugInfo("%_START_ WARNING: PPP received unknown packet type, sending code-reject! %_END_");

		break;
	case ser:
		/* Send an echo-reply packet */
		construct_LCP_Packet(echoReply, ++identifier, 0, &my_packet);
		makeServiceRequest(DATA, request, (char*)&my_packet, sizeof(Packet));
		break;
		
		/* Expand the multiple actions */
	case irc_scr:
		receivedAction(irc);
		receivedAction(scr);
		break;
	case irc_scj:
		receivedAction(irc);
		receivedAction(scj);
		break;
	case irc_str:
		receivedAction(irc);
		receivedAction(str);
		break;
	case irc_tlu:
		receivedAction(irc);
		receivedAction(tlu);
		break;	
	case sca_tlu:
		receivedAction(sca);
		receivedAction(tlu);
		break;
	case tld_scr:
		receivedAction(tld);
		receivedAction(scr);
		break;
	case irc_scr_sca:
		receivedAction(irc);
		receivedAction(scr);
		receivedAction(sca);
		break;
	case irc_scr_scn:
		receivedAction(irc);
		receivedAction(scr);
		receivedAction(scn);
		break;
	case tld_irc_str:
		receivedAction(tld);
		receivedAction(irc);
		receivedAction(str);
		break;
	case tld_scr_scn:
		receivedAction(tld);
		receivedAction(scr);
		receivedAction(scn);
		break;
	case tld_scr_sca:
		receivedAction(tld);
		receivedAction(scr);
		receivedAction(sca);
		break;
	case tld_zrc_sta:
		receivedAction(tld);
		receivedAction(zrc);
		receivedAction(sta);
		break;
	default:
		/* We should never get here! */
		abort();
	}
}

static char * construct_LCP_Packet(char code, char identifier, int length, Packet* packet) {
	lcp_packet_mask* pack = (lcp_packet_mask*)packet->Data;
	packet->Protocol = 0xc021;
	pack->lcpCode = code;
	pack->lcpIdentifier = identifier;
	pack->lcpLength = (unsigned short)length;
	return &(pack->data);
}

static void makeServiceRequest(Services service, Commands command, char* data, int size) {
	static ServiceRequest myServiceRequest;	
	myServiceRequest.Service = service;
	myServiceRequest.Command = command;

	*((unsigned short*)&(myServiceRequest.Data)) = myDlci;
	memcpy(((char*)&(myServiceRequest.Data))+2, data, size); 
	memset(((char*)&(myServiceRequest.Data))+2+size, 0, sizeof(myServiceRequest.Data)-2-size);

	ClearToSend_Post(0);
	if(!rfcommConnected || rfcommCts) {
		RFCOMMServiceRequestOut_Post(&myServiceRequest);
	} else  {
		toSend=&myServiceRequest;
	}
}