Skip to content

What a Lot of Sock(et)s

February 14, 2016

16-02-14 multi-socket
I spent hours yesterday down in the guts of the wiznet access routines and the doughnuts server making changes to access multiple sockets. At the very bottom, routines like wizCmd were changed to accept a socket number as an additional argument and to incorporate it in the opcode sent to the W5500 like
wizWrite(SnCR,WIZNET_WRITE_S0R|(sock<<5),&cmd,1);
All the rest of the code just had to change to accept and pass the socket number.

At this stage it’s still really only using socket 0 because main() onliy init’s, checks, and passes down that one but all the underpinnings are done and hopefully done in a way that I can build on. The next step will be to change main to init a number of sockets and check them in sequence. Should be easy.

Once I have the web server working I’ll make the changes to use telnet.

//14-11-26 routines to address the wiznet w5500
//14-11-27 switched to single read & write routines
//14-12-8 wrapper routines for reading and writing commands and registers
//16-01-22 allowing for little-endian ints from Z80
//16-02-14 w55mscode - multi-socket support
void wizWrite(unsigned int addr, unsigned char opcode, void * data, unsigned int len){
//variable length write to the wiznet W5500
//opcode is xxxx x100
// xxxxx is 00000 for common area, 00001 for socket 0 register, 00010 for socket 0 tx buffer
  enablewiz();   			// Activate the CS pin
  #ifdef __CPUTYPEZ80
  	spiSend((unsigned char)((addr>>8)&0xff)); //send high byte
  	spiSend((unsigned char)addr&0xff); //send low byte
  #else
  	spiSendN((unsigned char *)&addr,2); //1802 uses big endian ints - already in tetwork order
  #endif
  spiSend(opcode);   		// Send Wiznet Write OpCode
  spiSendN(data,len);		//Send the payload
  disablewiz();				// make CS pin not active
}

void wizRead(unsigned int addr,unsigned char opcode, void * data, unsigned int len){
//variable length read from the wiznet w5500
//opcode is xxxx x000
// xxxxx is 00000 for common area, 00001 for socket 0 register, 00011 for socket 0 rx buffer
  enablewiz();   			// Activate the CS pin
  #ifdef __CPUTYPEZ80
  	spiSend((unsigned char)((addr>>8)&0xff)); //send high byte
  	spiSend((unsigned char)addr&0xff); //send low byte
  #else
  	spiSendN((unsigned char *)&addr,2); //1802 uses big endian ints - already n tetwork order
  #endif
  spiSend(opcode);   		// Send Wiznet Read OpCode
  spiReceiveN(data,len);	// Send the data byte
  disablewiz();				// make CS pin not active
}
void wizCmd(unsigned char cmd,unsigned char sock){ //send a command to socket N and wait for completion
	wizWrite(SnCR,WIZNET_WRITE_S0R|(sock<<5),&cmd,1); //send the command
	while(wizRead(SnCR,WIZNET_READ_S0R|(sock<<5),&cmd,1),cmd); //wait til command completes
}
void wizSetCtl8(unsigned int ctlreg, unsigned char val,unsigned char sock){//write to a socket 0 control register
	wizWrite(ctlreg, WIZNET_WRITE_S0R|(sock<<5),&val,1);
}
unsigned char wizGetCtl8(unsigned int ctlregaddr,unsigned char sock){
  unsigned char regval; //spot to hold the register contents
  wizRead(ctlregaddr,WIZNET_READ_S0R|(sock<<5),&regval,1);
  return regval;
}
void wizSetCtl16(unsigned int ctlregaddr, unsigned int ctlregvalue,unsigned char sock){
  #ifdef __CPUTYPEZ80 //distinguish little-endian ints
  	wizWrite(ctlregaddr,WIZNET_WRITE_S0R|(sock<<5),(unsigned char *) &ctlregvalue+1,1); //high byte
  	wizWrite(ctlregaddr+1,WIZNET_WRITE_S0R|(sock<<5),(unsigned char *) &ctlregvalue,1); //low byte
  #else
  	wizWrite(ctlregaddr,WIZNET_WRITE_S0R|(sock<<5),(unsigned char *) &ctlregvalue,2);
  #endif
}
unsigned int wizGetCtl16(unsigned int ctlregaddr, unsigned char sock){
  union WReg regval; //spot to hold the register contents
  #ifdef __CPUTYPEZ80 //distinguish little-endian ints
  	wizRead(ctlregaddr,WIZNET_READ_S0R|(sock<<5),regval.c+1,1); //high byte
  	wizRead(ctlregaddr+1,WIZNET_READ_S0R|(sock<<5),regval.c,1); //low byte
  #else
  wizRead(ctlregaddr,WIZNET_READ_S0R|(sock<<5),regval.c,2);
  #endif
  return regval.i;
}
void wiz_Init(unsigned char ip_addr[]){// Ethernet Setup
  unsigned char readback_ip[] = {1,2,3,4};
  wizWrite(SIPR,WIZNET_WRITE_COMMON,ip_addr,4);// Set the Wiznet W5100 IP Address
	delay(5);
  wizRead(SIPR,WIZNET_READ_COMMON,readback_ip,4); //read back the IP to make sure it "took"
  printf("Done Wiznet W5500 Initialization on IP address %d.%d.%d.%d\n",readback_ip[0],readback_ip[1],readback_ip[2],readback_ip[3]);
  wizRead(GAR,WIZNET_READ_COMMON,readback_ip,4); //read back the Gateway IP to make sure it "took"
  printf("Gateway Address %d.%d.%d.%d\n\n",readback_ip[0],readback_ip[1],readback_ip[2],readback_ip[3]);
}
void socket_init(unsigned char sock,unsigned char port){ //initialize socket for TCP server on specified port
	wizCmd(CR_CLOSE,sock); //make sure port is closed
	wizSetCtl8(SnIR,0xFF,sock); //reset interrupt reg
	wizSetCtl8(SnMR,MR_TCP,sock); //set mode register to tcp
	wizSetCtl16(SnPORT,port,sock); //set tcp port to 80
	wizCmd(CR_OPEN,sock); //open the port
	wizCmd(CR_LISTEN,sock); //listen for a conection
}

unsigned int send(unsigned char *buf,unsigned int buflen,unsigned char sock){
    unsigned int timeout,txfree; //txsize, removed 16-01-24
    unsigned char crsend=CR_SEND; //,crreadback removed 16-01-24
	unsigned int txwr;
    if (buflen <= 0) return 0; //make sure there is something to send
    //make sure there is room in the transmit buffer for what we want to send
    txfree=wizGetCtl16(SnTX_FSR,sock); //this is the size of the available buffer area
    timeout=0;
    while (txfree < buflen) {
      delay(1);
     txfree=wizGetCtl16(SnTX_FSR,sock);
     if (timeout++ > 1000) {// Timeout for approx 1 sec
       	printf("TX Free Size Error!\n");
		wizCmd(CR_DISCON,sock);// Disconnect the connection
       	return 0;
     }
   }
	//space is available so we will send the buffer
   	txwr=wizGetCtl16(SnTX_WR,sock);  // Read the Tx Write Pointer
   	wizWrite(txwr,WIZNET_WRITE_S0TX,buf, buflen); //write the outgoing data to the transmit buffer
   	wizSetCtl16(SnTX_WR|(sock<<5),txwr+buflen,sock);//update the buffer pointer
	wizCmd(CR_SEND,sock); // Now Send the SEND command which tells the wiznet the pointer is updated
    return 1;
}

unsigned int recv(unsigned char *buf,unsigned int buflen,unsigned char sock){
	unsigned int rxrd;
    if (buflen <= 0) return 1;
    if (buflen > MAX_BUF)	// If the request size > MAX_BUF,just truncate it
        buflen=MAX_BUF - 2;
    rxrd = wizGetCtl16(SnRX_RD,sock); // get the address where the wiznet is holding the data
	wizRead(rxrd,WIZNET_READ_S0RX|(sock<<5),buf,buflen); //read the data
    *(buf+buflen)='\0';        // terminate string
    return 1;
}

void flush(unsigned int rsize,unsigned char sock){ //this just gets rid of data that i don't want to process
	unsigned int rxrd;
	if (rsize>0){
  		rxrd=wizGetCtl16(SnRX_RD,sock); //retrieve read data pointer
  		wizSetCtl16(SnRX_RD,rxrd+rsize,sock); //replace read data pointer
		wizCmd(CR_RECV,sock); //tell the wiznet we`ve retrieved the data
	}
}
long getip(unsigned char sock){ //retrieve the requester's ip and return it as a long
	union IPaddr thisip;
	wizRead(SnDIPR,WIZNET_READ_S0R|(sock<<5),thisip.c,4);
	return thisip.l;
}
int sendStr(char* what,unsigned char sock){
	return send((unsigned char *)what,strlen(what),sock);
}
void sendnak(unsigned char sock){
	sendConst("HTTP/1.1 404 Not Found\r\n\r\n",sock); 	// Now Send the HTTP Response
}

void sendack(unsigned char sock){
	sendConst("HTTP/1.0 200 OK\r\n\r\n",sock); 	// Now Send the HTTP Response
}


/*****************************************************************************
//  File Name    : doughnutsms.c
//  Version      : 1
//  Description  : Bagels Number Guessing Game for Wiznet w5500 - multisocket version
//  Author       : Bill Rowe
//  Target       : Olduino/Z
//  Compiler     : SDCC/Z88DK
//  Created		 : Mar 12, 2015
*****************************************************************************/
//15-03-13 created based on bagels3 with change to doughnuts
//15-03-16 fixed logic error in response analysis
//16-02-14 updated forZ80, multi-socket operation

#include <stdio.h> //for printf etc.
#include <olduino.h> //for digitalRead, digitalWrite, delay
#include <cpuz80spd4port40.h>
#include <hspi3.h> //hardware spi header
#include <w5500data.h>  //wiznet definitions
#include <w55mscode.h>  //wiznet code definitions
#include <secrettable.h>  //code definitions for the table of secrets
#include "doughnutsms.h"
#include <stdlib.h>  //wiznet code definitions
#include <string.h>  //wiznet code definitions
#ifdef __CPUTYPEZ80
	char * itoa10(int i,char *buf){
		itoa(i,buf,10);
		return buf;
	}
#else
	#define itoa10(i,buf) itoa(i,buf)
#endif

#define MAX_BUF 512
unsigned char buf[MAX_BUF];			//memory buffer for incoming & outgoing data
unsigned char secret[4]="145";
unsigned char guess [4]={0};
unsigned int pages=0, games=0, sessions=0;
char pnbuf[8];
unsigned int tbrnd(){//random numbers ala tiny basic R:=R*2345+6789
	static unsigned int R=1;
	R=R*58653+13849;//R=R*2345+6789;
	//printf("%d\n",R);
	return R;
}
void setsecret(){//set the global secret to a 3 digit random number
	int i=0,d1=0,d2,d3;
	while(d1==0){
		d1=tbrnd()%10;
	}
	d2=tbrnd()%10;
	while(d1==d2){
		d2=tbrnd()%10;
	}
	d3=tbrnd()%10;
	while(d3==d1||d3==d2){
		d3=tbrnd()%10;
	}
	secret[0]=d1+0x30;secret[1]=d2+0x30;secret[2]=d3+0x30;secret[3]=0;
	printf("secret=%s\n",secret);
}
void bagelsinit(unsigned char sock){//initialize bagels game for socket N
	games++;
	pages++;
	sendConst(hdr1a,sock);sendStr(itoa10(pages,pnbuf),sock); sendConst(hdr1b,sock); 	// Now Send the HTTP Response first part
	sendConst(Inst1,sock); 	// Now Send the instructions
	sendConst(Inst2,sock); 	// Now Finish the instructions
	sendConst(gform,sock); 	// Now Send the rest of the page
	sendConst(olduinolink,sock);
	sendConst(trlr,sock); 	// Now Send the trailer
	thisip.l=getip();
	thisipslot=getipslot(thisip.l);//finds or assigns a slot for the ip
	setsecret();
	strcpy((char*)secrets[thisipslot],(char*)secret);
	printf("IP: %d.%d.%d.%d,slot %d,secret %s\n",
		thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3],thisipslot,secrets[thisipslot]);
}
void sendip(unsigned char * ip,unsigned char sock){
	char strbuf[4];
	sendStr(itoa10(ip[0],strbuf),sock);
	sendConst(".",sock);
	sendStr(itoa10(ip[1],strbuf),sock);
	sendConst(".",sock);
	sendStr(itoa10(ip[2],strbuf),sock);
	sendConst(".",sock);
	sendStr(itoa10(ip[3],strbuf),sock);
}
void sendtable(unsigned char sock){
	unsigned int i;
	char buf[8];
	sendConst("<b>Client Table/Secret/Games/Turns</b><br>",sock);
	for (i=0;i<numslots;i++){
		if (regip[i].c[0]!=0){
			sendip(regip[i].c,sock);
			sendConst("/",sock);
			sendStr((char *)secrets[i],sock);
			sendConst("/",sock);
			sendStr(itoa10(ipgames[i],buf),sock);
			sendConst("/",sock);
			sendStr(itoa10(ipturns[i],buf),sock);
			sendConst("<br>",sock);
		}
	}
}
//unsigned char secrets[numslots][4]={0}; //secret numbers for each registered ip
//unsigned int ipturns[numslots];//number of moves for the IP
//unsigned int ipgames[numslots];//number of games won by IP
void bagelspeek(unsigned char sock){//show the ip table
	pages++;
	sendConst(hdr1a,sock);sendStr(itoa10(pages,pnbuf),sock); sendConst(hdr1b,sock); 	// Now Send the HTTP Response first part
	sendtable(sock);
	sendConst(trlr,sock); 	// Now Send the rest of the page

}
void congratulate(unsigned char sock){
	char strbuf[8];
	ipgames[thisipslot]++;//increment win count
	sendConst("You have won ",sock);
		sendStr(itoa10(ipgames[thisipslot],strbuf),sock);
	sendConst(" games ",sock);
	if (ipgames[thisipslot]>maxipgames){//high scode
		maxipgames=ipgames[thisipslot];
		maxip.l=thisip.l;
		sendConst(" - A new high score!",sock);
	}else{
		sendConst("<p>",sock);
		sendConst("High score is ",sock); sendip(maxip.c,sock); sendConst(" with ",sock);
		sendStr(itoa10(maxipgames,strbuf),sock);
		sendConst(" wins<p>",sock);
	}
}
void sendrespform(int pico, int fermi,unsigned char sock){
	int i;
	pages++;
	sendConst(hdr1a,sock);sendStr(itoa10(pages,pnbuf),sock); sendConst(hdr1b,sock); 	// Now Send the HTTP Response first part
	sendConst("Your Guess Was: ",sock); 	// Now Send the response
	sendStr((char *)guess,sock);
	sendConst("  I say ",sock);
	if (fermi==3){
		sendConst("YOU GOT IT!<P>",sock);
		congratulate(sock);
		sendConst(pform,sock);
		sendConst(olduinolink,sock);
		sendConst(trlr,sock); 	// Now Send the rest of the page
	} else {//once we're here we're going to send the guess form
		if (pico<0){//flag for duplicate digits
			sendConst("<P>Oh, maybe I didn't tell you.  No two digits are the same.<P>",sock);
		} else if ((pico+fermi)==0){
			sendConst("Doughnuts!",sock);
		} else{
			for(i=0;i<pico;i++) sendConst("Pico ",sock);
			for(i=0;i<fermi;i++) sendConst("Fermi ",sock);
		}
		sendConst("<P>",sock);
		sendConst(gform,sock); 	// Now Send the guess form
		sendConst(Reminder,sock);//send the reminder
		sendConst(Inst2,sock);
		sendConst(olduinolink,sock);
		sendConst(trlr,sock); 	// Now Send the rest of the page
	}
}
unsigned int cpyguess(unsigned char *to,unsigned char *from){//copy the guess until space or length limit
	unsigned int len=0;
	while((len<3) && *from!=' ' &&*from!='+' &&*from!='%'){
		*to++=*from++;
		len++;
	}
	*to=0;
	return len;
}
void bagelsturn(unsigned char sock){
	int i, /*ipslot,*/ pico=0, fermi=0;
	thisipslot=getipslot(thisip.l);
	if(strcmp(secrets[thisipslot],"999")){//as long as we have an actual secret
		ipturns[thisipslot]++; //count the turn
		printf("Guess: %d.%d.%d.%d,slot %d,secret %s\n",
			thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3],thisipslot,secrets[thisipslot]);
		strcpy((char*)secret,(char*)secrets[thisipslot]);
		cpyguess(guess,buf+8);
		for (i=0;i<3;i++){
			if (guess[i]==secret[i]){
				fermi++;
			}
		}
		if (guess[0]==secret[1]||guess[0]==secret[2]){pico++;}
		if (guess[1]==secret[0]||guess[1]==secret[2]){pico++;}
		if (guess[2]==secret[0]||guess[2]==secret[1]){pico++;}
		if (guess[0]==guess[1]||guess[0]==guess[2]||guess[1]==guess[2]||strlen((char *)guess)!=3){pico=-1;}
		sendrespform(pico, fermi,sock);
	}else{
		printf("Uninitialized secret for IP %d.%d.%d.%d in slot %d\n",
			thisip.c[0],thisip.c[1],thisip.c[2],thisip.c[3],thisipslot);
		bagelsinit(sock);
	}
}

void handlesession(unsigned char sock){	//handle a session once it's established
	unsigned int rsize;
	unsigned int tries=10;
	rsize=wizGetCtl16(SnRX_RSR,sock); //get the size of the received data
	while(rsize==0 && tries-->0){
		delay(20);
		printf("re-size ");
		rsize=wizGetCtl16(SnRX_RSR,sock); //retry size of the received data
	}
	printf("**rsz=%d\n",rsize);
	if (rsize>0){
		thisip.l=getip();
		if (recv(buf,min(16,rsize),sock)>0){ //get enough characters to distinguish the request
			printf("%s\n",buf);
  			if (strncmp((char *)buf,"POST /",6)==0){
  				bagelsinit(sock); //initialize game, send the form
			}
			else if (strncmp((char *)buf,"GET /favicon",12)==0){
  				sendnak(sock); //no favicon here
			}
  			else if (strncmp((char *)buf,"GET /?G=",8)==0){
				bagelsturn(sock);	//give player his turn
			}
  			else if (strncmp((char *)buf,"GET /T",6)==0){
 				bagelspeek(sock); //show the IP table
			}
  			else if (strncmp((char *)buf,"GET /",5)==0){
 				bagelsinit(sock); //initialize game, send the form
			}
  			else{
				printf("\nmystery meat\n");
 				bagelsinit(sock); //initialize game, send the form
			}
		}
	}
	printf("flushing %d\n",rsize);
  	if (rsize>0) flush(rsize,sock);	//get rid of the received data
	wizCmd(CR_DISCON,sock);// Disconnect the connection- we're done here
	printf("done\n>\n");
	sessions++;
}

void main(void){
	unsigned char socket0status;
    unsigned char ip_addr[] = {192,168,1,63};//{169,254,180,2};////{10,0,0,180};//
    unsigned char port=80; //tcp/ip port
    unsigned int SFWs=0; //count of times we've seen SOCK_FIN_WAIT
    printf("Olduino/Z Doughnuts Server - Multi Socket 1.0\n");
	delay(500);
	spiInit(); //initialize the tp3465
    wiz_Init(ip_addr); //initialize the wiznet chip
	while(1){  // Loop forever
		socket0status=wizGetCtl8(SnSR,0); //socket 0 status
		switch (socket0status){
			case SOCK_CLOSED: //initial condition
				SFWs=0;
				socket_init(0,port);	//initialize socket 0
				break;
			case SOCK_ESTABLISHED: //someone wants to talk to the server
				SFWs=0;
				handlesession(0);
				printf("%d sessions, %d games, %d pages\n",sessions,games,pages);
				break;
			//following are cases where we have to reset and reopen the socket
			case SOCK_FIN_WAIT:
				printf("SOCK_FIN_WAIT:");
				if (++SFWs>2){
					printf(" lost patience, closing\n");
					wizCmd(CR_CLOSE,0);
				}else{
					printf(" ignoring\n");
				}
				break;
			case SOCK_CLOSING: case SOCK_TIME_WAIT:
			case SOCK_CLOSE_WAIT: case SOCK_LAST_ACK:
				SFWs=0;
				wizCmd(CR_CLOSE,0);
				break;
		}
		tbrnd(); //cycle the generator
		delay(100);
	}
}
#include <olduino.c>
#include <hspi3.c>
#include <w55mscode.c>
#include <secrettable.c>
Advertisements

From → Olduino/Z, web server

2 Comments
  1. There is also a way to inline small functions in sdcc. I’m looking specifically at this short piece of code:

    #ifdef __CPUTYPEZ80
    char * itoa10(int i,char *buf){
    itoa(i,buf,10);
    return buf;
    }
    #else

    If this is changed to:

    #ifdef __CPUTYPEZ80
    inline char * itoa10(int i,char *buf){
    itoa(i,buf,10);
    return buf;
    }
    #else

    the generated code should be better.

  2. thanks – I’ll try that. The big improvement is to get the outbound socket driver working and use printf for the whole line instead of having to call atoi() etc.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: