Ethernet 7/7
Checksum
The checksum is a method to control the packet integrity, or rather permits to establish if this contains errors (and so is unusable).
the word checksum doesn't identify a particular algorithm, but that used for IP and others protocols is the complement to 1 of the complements sum to 1 of the words at 16bit of the dates where it is made the calculation;
or rather, they sum all the words at 16bit, summing also the eventual overflow and at last follow the complement at 1.
Fisrt it was illustrated how the checksum, in the sending phase, is calculated and then wrote in the packet; what wasn't still illustrated is the checksum control at the packet arrive. To verify that the packet is integral, must save temporally the packet checksum field and put at zero, then follow the calculation and then confront the result with that one saved; if are the same, the packet is correct.
Actually ,for how is made the algorithm, the verifying in very simple: it's enough to calculate the checksum in the packet without modify it; if the result is zero, so the packet in OK, or else is for reject.
The ENC28J60 integral, in the DMA module, a function quite fast for the checksum calculation, and we'll use it.
Unfortunatelly,
in the chip B5 review, this function gives big problems to the packets reception, so in this case the calculation will follow the software.
First of all, let's see the function prototype DMAChecksum:
u16 DMAChecksum(u16 start, u16 len, BOOL rx)
the first parameter indicates the address from where begin the dates, len the length in byte of these dates, at last rx is useful to indicate
if the address "talks" about the RX or TX .
(The address is an offset relative, compared with the MAC packet dates field beginning )
As I told first, this method has two different implementations for the review B1-B4 e B5, let's see the first one:
u16 DMAChecksum(u16 start, u16 len, BOOL rx){
// calculates the checksum using the chip function
u16 tmp;
u8 L,H;
if (rx) {
tmp = TX_BUF_START + 1 + sizeof(MAC_Header) + start;
} else {
tmp = packetStart + 6 + sizeof(MAC_Header) + start;
if (tmp > RX_BUF_END)
tmp = tmp - RX_BUF_END + RX_BUF_START - 1;
}
setBank(0);
writeReg(EDMASTL, LOW(tmp));
writeReg(EDMASTH, HIGH(tmp));
tmp = tmp+len-1; // end packet
if (!rx && tmp > RX_BUF_END)
tmp = tmp - RX_BUF_END + RX_BUF_START - 1;
writeReg(EDMANDL, LOW(tmp));
writeReg(EDMANDH, HIGH(tmp));
BFSReg(ECON1, 0b00110000); //start calculation
while(readETH(ECON1) & 0b00100000); // wait end calculation
tmp = (u16)readETH(EDMACSL) << 8;
tmp = tmp | readETH(EDMACSH);
return tmp; // return the checksum calculated
}Fist of all, are calculated and wrote in the registers the absolute start and end addresses of the dates on we make the operation. Then is passed the calculation and we are waiting for the end.
In the second case, is followed the calculation on the software, which makes the operation very slow:
u16 DMAChecksum(u16 start, u16 len, BOOL rx){
// calculate the checksum via software
u16 tmp;
u16 reg[2];
u32 sum;
u16 len2;
int i;
if (rx) {
tmp = TX_BUF_START + 1 + sizeof(MAC_Header) + start;
} else {
tmp = packetStart + 6 + sizeof(MAC_Header) + start;
if (tmp > RX_BUF_END)
tmp = tmp - RX_BUF_END + RX_BUF_START - 1;
}
// salva ERDPT
setBank(0);
reg[0] = readETH(ERDPTL);
reg[1] = readETH(ERDPTH);
writeReg(ERDPTL,LOW(tmp));
writeReg(ERDPTH,HIGH(tmp));
sum = 0;
len2 = len & 0xFE;
CS = 0;
spiWrite(RBM);
for (i=0; i<len2; i=i+2){
tmp = ((u16)spiRead()) << 8 | spiRead();
sum = sum + (u32) tmp;
}
if (len2!=len) sum += ((u32)spiRead()) << 8; // if the packet has odd length
CS = 1;
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
tmp = ~sum;
// return ERDPT;
setBank(0);
writeReg(ERDPTL,reg[0]);
writeReg(ERDPTH,reg[1]);
return htons(tmp); // return the checksum calculated
}Before effecting the operation, is saved the ERDPT register (restored at the end ), that's why are read
all the byte necessary to the calculation.
Let's see also the method putChecksum used in precedence:
void putChecksum(u16 offset, u16 sum){
// write the checksum "sum" to the address "offset" to the writing buffer internal
u16 tmp;
u16 addr[2];
setBank(0);
addr[0] = readETH(EWRPTL); // save temporally the punter
addr[1] = readETH(EWRPTH); // of the writing
tmp = 1+sizeof(MAC_Header)+offset; // new address
writeReg(EWRPTL,LOW(TX_BUF_START+tmp));
writeReg(EWRPTH,HIGH(TX_BUF_START+tmp)); // charge the punter
encPut(LOW(sum));
encPut(HIGH(sum)); // write the checksum
writeReg(EWRPTL,addr[0]);
writeReg(EWRPTH,addr[1]); // restore the old pinter}
ICMP
The ICMP is a service protocol (descried from the RFC 792), used to effectuate the controls and the signals inside a network. An ICMP packet can transport different messages types, but that most known and the unique treat in here, is the Echo request, known also like Ping.
In the ISO/OSI model this protocol is usually insert to the third level, doesn't being properly a transport protocol, but it is found above the IP protocol inside of this one is encapsulated.
The packet structure
The ICMP packet structure varies upon the transported message, with the exception of the heading, common to all the types.
Let's see the particular case of an eco request:

- Type: takes the value 8 for the request, while in the answer packet is 0.
- Code: unused.
- Checksum: By now known the control field. The algorithm used is the same with the IP protocol..
- Identifier e Sequence Number: are generated by the applicant to recognize the answer. Are left unchanged from who respond.
- Data: contains the dates variable number that are turned in in the answer.
The code
First of all let's see the content of icmp.h file that contains some define, but especially the ICMP packet definition.
#define ICMP_ECHO 8
#define ICMP_ECHO_REPLY 0
#define MAX_ICMP_DATA 32 // the maximum length of the dates field
typedef struct {
u8 type;
u8 code;
u16 checksum;
u16 id;
u16 sn;
u8 data[MAX_ICMP_DATA];
} ICMPPacket;
void processICMP(IP_Header ipHeader);Like you can see are presented the different fields described in the previous paragraph.
The icmp.c file contains the code that permits to answer to a ping.
#include "stack.h"
#define ICMP_Offset sizeof(IP_Header)
void processICMP(IP_Header ipHeader){
ICMPPacket packet;
u8 size;
size = ipHeader.totalLength - (ipHeader.verlen & 0x0F)*4;
if (size > sizeof(packet)) size = sizeof(packet);
encGetArray((u8*)&packet, size);
if (packet.type == ICMP_ECHO){
packet.type = ICMP_ECHO_REPLY;
packet.checksum = 0;
IPPutHeader(ipHeader.sourceIP, IPPROTO_ICMP, (u8*)&packet, size, size);
putChecksum(ICMP_Offset+2,DMAChecksum(ICMP_Offset,size,TRUE));
MACSend();
}
}Through the encGetArray method the packet is read and saved in the variable packet . Then, once examined the type field, if this results to be an eco request, is built the answer packet:
the type field is set to 0 (Echo Reply),the checksum is reseted to zero and recalculated once that the packet is wrote in the buffer; at last is sent everything withMACSend.
ProcessPacket
To functioning everything, must implement a method that examines the packets in arrived and send them to the relatives administrator. (IP and ARP for the third level).
This method is processPacketans is recalled inside a infinite loop in the method main:
void main() {
encInit();
while (1)
processPacket();
}Let's see the stack.c file
#include "stack.h"
MACAddr remoteAddr;
void processPacket(){
MAC_Header header;
setBank(1);
while (readETH(EPKTCNT)) { // if there is almost a packet
MACGetHeader(&header);
if (header.type == TYPE_IP){
remoteAddr = header.sourceMAC;
processIP();
} else
if (header.type == TYPE_ARP)
processARP();
freeRxSpace(); // free the space in the RX buffer
}
}Reading the EPKCNT register we verify that there is almost a packet waiting to be read, then with the MACGetHeader is read the MAC heading and through the type field is selected if call processIP or proccessARP (or none of these).
These methods were treat in the previous pages.
- Chris's blog
- 1192 reads





Post new comment