Embedded Ethernet: Part 3

Today I’m posting the MAC layer. The stack is written like, well a stack. This layer (MAC) sits on top of the ENC layer that controls the ethernet controller. Above the MAC layer sits the IP layer which I’ll post next.

Previously in the Embedded Ethernet Series…
Part 1
Part 2

MAC.h

// MAC.h
#ifndef _H_MAC
#define _H_MAC

#include <p32xxxx.h>
#include "../globals.h"
#include "../static.h"
#include "../GenericTypeDefs.h"
#include "../controlboard.h"

#include "../ENCDefs.h"

typedef union _ET
{
	WORD w;
	BYTE v[2];
} ET;

typedef struct _MAC_HEADER
{
	BYTE		D_MAC[6];
	BYTE		S_MAC[6];
	ET			EtherType;
	WORD		PayLen;
} MAC_HEADER;	

#include "IPSuite.h"

void MAC_FormHeader(MAC_HEADER*, BYTE*, WORD);	

void MAC_TxHeader(MAC_HEADER*);

void MAC_TxArray(unsigned char*, unsigned int);	

void MAC_Flush(void);

void MAC_TxPredefined(void);

void MAC_ParseBuffer(void);

void MAC_PrintHeader(MAC_HEADER *);

void MAC_Encapsulate(unsigned char *, unsigned short, unsigned char *, unsigned int);

#endif

MAC.c

// MAC.c
#include "MAC.h"

// from control.c
extern unsigned char HW_ADDRESS[6];
extern unsigned char HW_BROADCAST[6];

//from ENC28J60.c
extern unsigned char rx_buffer[4][2048];
extern unsigned char rx_buffer_rd;

//! Forms a MAC_HEADER (initializes)
/*!
 \param hdr Pointer to MAC_HEADER
 \param dmac Destination MAC Address (6 byte array)
 \param ET EtherType
 */
void MAC_FormHeader(MAC_HEADER* hdr, BYTE* dmac, WORD ET)
{
	unsigned char i;

	// Copy Destination MAC
	for(i=0; i<6; i++){	hdr->D_MAC[i] = dmac[i]; }

	// Copy Source MAC (use own)
	for(i=0; i<6; i++){	hdr->S_MAC[i] = HW_ADDRESS[i]; }

	// Copy EtherType
	hdr->EtherType.w = ET;
}

//! Transmits a MAC_HEADER
/*!
 All data must be in the MAC_HEADER, this just transmits what is there.
 \param hdr MAC_HEADER to transmit
 */
void MAC_TxHeader(MAC_HEADER* hdr)
{
	unsigned char i;

	// Reset ENC
	ENC_TxPrepare();

	// Destination MAC
	for(i=0; i<6; i++){	ENC_WriteBufferMemory(hdr->D_MAC[i]); }

	// Source MAC
	for(i=0; i<6; i++){	ENC_WriteBufferMemory(hdr->S_MAC[i]); }

	// Ethertype
	ENC_WriteBufferMemory(hdr->EtherType.v[1]);
	ENC_WriteBufferMemory(hdr->EtherType.v[0]);
}

//! Transmits an array of bytes to the PHY’s TxBuffer (ENC Controller)
/*!
 A MAC_HEADER must be transmitted first via the MAC_TxHeader() call.
 \param a Array
 \param len Length of Array
 \sa MAC_TxHeader()
 */
void MAC_TxArray(unsigned char* a, unsigned int len)
{
	unsigned int i;

	for(i=0; i<len; i++)
	{
		ENC_WriteBufferMemory(a[i]);
	}
}

//! Initiates the PHY to send
/*!
 \param a Array
 \param len Length of Array
 */
void MAC_Flush(void)
{
	ENC_TxSend();
}

//! Creates and sends a predefined packet
void MAC_TxPredefined(void)
{
	unsigned char PredefinedPacket[2] = {0×57, 0×57};

	MAC_HEADER MAC_Head;

	MAC_FormHeader(&MAC_Head, HW_BROADCAST, sizeof(PredefinedPacket));

	MAC_TxHeader(&MAC_Head);

	// Payload
	MAC_TxArray(PredefinedPacket, 2);

	MAC_Flush();
}

//! Parses an array into a MAC_PACKET (Header and Payload)
/*!
 Called when a buffer has been passed up from the PHY (ENC Controller), parse it
 */
void MAC_ParseBuffer(void)
{
	unsigned int i, ptr;
	unsigned char lo = 2;		// Length offset

	#ifdef DEBUG_RXRING
	printf("Handling Buffer %d\r\n", rx_buffer_rd);
	#endif

	unsigned int len = form16(rx_buffer[rx_buffer_rd][0], rx_buffer[rx_buffer_rd][1]);
	//printf("[[%04x]]\r\n", len);

	RXSTATUS RX_Status;
	MAC_HEADER MAC_H;

	ptr = lo;
	for(i=0; i<4; i++){
		RX_Status.v[i] = rx_buffer[rx_buffer_rd][ptr];
		ptr++;
	}

	for(i=0; i<6; i++){
		MAC_H.D_MAC[i] = rx_buffer[rx_buffer_rd][ptr];
		ptr++;
	}

	for(i=0; i<6; i++){
		MAC_H.S_MAC[i] = rx_buffer[rx_buffer_rd][ptr];
		ptr++;
	}

	for(i=0; i<2; i++){
		MAC_H.EtherType.v[i] = rx_buffer[rx_buffer_rd][ptr];
		ptr++;
	}
	MAC_H.EtherType.w = swaps(MAC_H.EtherType.w);

	// Take out the Check Sequence (4 bytes)
	MAC_H.PayLen = (len-ptr)-4;

	#if DL_MAC == 6
	MAC_PrintHeader(&MAC_H);
	#endif

	#if DL_MAC == 8
	print_array(rx_buffer[rx_buffer_rd], lo, lo+len, 16);
	#endif

	//if(array_compare(MAC_H.D_MAC, HW_ADDRESS, 6))
	if((array_compare(MAC_H.D_MAC, HW_ADDRESS, 6)) || (array_compare(MAC_H.D_MAC, HW_BROADCAST, 6)))
	{
		if(array_compare(MAC_H.D_MAC, HW_ADDRESS, 6))
		{
			#if DL_MAC == 4
			MAC_PrintHeader(&MAC_H);
			print_array(rx_buffer[rx_buffer_rd], lo, lo+len, 16);
			#endif
		}

		switch(MAC_H.EtherType.w)
		{
			case MAC_ET_IP:
				#ifdef DEBUG_MAC_PrintType
				printf("IP Packet.\r\n");
				#endif
				IP_ParsePayload(&MAC_H, (unsigned char*)((rx_buffer[rx_buffer_rd])+ptr), MAC_H.PayLen);
				break;
			case MAC_ET_ARP:
				#ifdef DEBUG_MAC_PrintType
				printf("ARP Packet.\r\n");
				#endif
				ARP_ParsePayload(&MAC_H, (unsigned char*)((rx_buffer[rx_buffer_rd])+ptr), MAC_H.PayLen);
				break;
			case MAC_ET_VLAN:
				#ifdef DEBUG_MAC_PrintType
				printf("VLAN-Tagged Frame (Network Bridge).\r\n");
				#endif
				break;
			case MAC_ET_IPv6:
				#ifdef DEBUG_MAC_PrintType
				printf("IPv6 Packet.\r\n");
				#endif
				break;
			case MAC_ET_VOIP:
				#ifdef DEBUG_MAC_PrintType
				printf("LLDP VOIP Discovery Packet.\r\n");
				#endif
				break;
			default:
				#ifdef DEBUG_MAC_PrintType
				printf("Unknown Packet Type (%04x).\r\n", MAC_H.EtherType.w);
				#endif
				break;
		}
	}
	rx_buffer_rd++;
	if(rx_buffer_rd >= 4){ rx_buffer_rd = 0; }
}

//! Prints a MAC_HEADER
/*!
 \param h MAC_HEADER to be printed
 */
void MAC_PrintHeader(MAC_HEADER *h)
{
	unsigned int i;

	printf("Print MAC Packet\r\n");
	printf("  Destination MAC:………"); printsep(h->D_MAC, 6, ‘x’, ‘-’); printf("\r\n");
	printf("  Source MAC:………….."); printsep(h->S_MAC, 6, ‘x’, ‘-’); printf("\r\n");
	printf("  EtherType:……………%04x\r\n", h->EtherType.w);
	printf("  Payload Length:……….%04x\r\n", h->PayLen);
	printf("\r\n");
}

void MAC_Encapsulate(unsigned char *DestIP, unsigned short ET, unsigned char *data, unsigned int datalen)
{
	unsigned int row;
	unsigned char THA[6];

	// Determine MAC
	if(ARP_Find(DestIP))
	{
		// If I got here, there is a ARP entry
		row = ARP_Table_Locate(DestIP);
		ARP_Fetch(row, THA);

		// Create MAC Header
		MAC_HEADER h_m;
		MAC_FormHeader(&h_m, THA, ET);
		h_m.PayLen = datalen + 6 + 6 + 2;	

		// Print MAC Header
		#ifdef DOUT_MAC
		printf("Outgoing Header:\r\n");
		MAC_PrintHeader(&h_m);
		#endif

		// Transmit MAC Header
		MAC_TxHeader(&h_m);

		// Transmit MAC Payload (IP / ARP Packet)
		MAC_TxArray(data, datalen);

		// Initiate Transmission
		MAC_Flush();
	}
	else
	{
		printf("MAC: Attempted to Encapsulate a Packet with no ARP Entry. \r\n");
	}

}
Posted Wednesday, May 20th, 2009 under firmware, projects.

One comment so far

  1. THANKS FOR THE GREAT BLOG, LOOKING FORWARD TO SEE IP LAYER CODE.

Leave a Reply