/*******************************************************************************
        Samples\tcpip\UCOS\ucos2.c
        Z-World, 2000

        A sample data-gathering app, that combines a task reading from the
        serial port with another task to report this data to the user over the
        network, with associated local messaging and processing.

*******************************************************************************/
#class auto
 
/***********************************
 * Configuration                   *
 * -------------                   *
 * All fields in this section must *
 * be altered to match your local  *
 * network settings.               *
 ***********************************/

/*
 * Pick the predefined TCP/IP configuration for this sample.  See
 * LIB\TCPIP\TCP_CONFIG.LIB for instructions on how to set the
 * configuration.
 */
#define TCPCONFIG 1

/* Serial port configuration */

/*
 * The Speed at which to run the serial port
 */
#define SERIAL_PORT_SPEED 19200L

/*
 *		Serial port Settings
 *		Serial PORT 1=A, 2=B, 3=C, 4=D
 * 	The size of the read/write buffers in the serial port driver.
 * 	You probably don't have to change this
 *
 */

#define SERIAL_PORT	2
#define INBUFSIZE		1023
#define OUTBUFSIZE 	31

/* MCOS configuration */
#define OS_MAX_TASKS           2  		// Maximum number of tasks system can create (less stat and idle tasks)
#define OS_SEM_EN					 1			// Enable semephores
#define OS_SEM_POST_EN			 1       // Enable old style post to semaphore
#define OS_MAX_EVENTS			 7			// MAX_TCP_SOCKET_BUFFERS + 2 + 1 (1 semaphore is used in this app)
#define STACK_CNT_256			 2			// LED update task + main()
#define STACK_CNT_2K         	 1			// TCP/IP needs a 2K stack


/********************************
 * End of configuration section *
 ********************************/

/*
 * 	Select serial port to use.
 */

#if (SERIAL_PORT==1)

	#define serXopen  	serAopen
	#define serXread  	serAread
	#define serXgetc  	serAgetc
	#define serXrdUsed  	serArdUsed
	#define serXwrite 	serAwrite
	#define serXclose 	serAclose
	#define serXwrFlush	serAwrFlush
	#define serXrdFlush	serArdFlush
	#define serXwrFree	serAwrFree
	
	#define AINBUFSIZE	INBUFSIZE
	#define AOUTBUFSIZE	OUTBUFSIZE
	
#elif (SERIAL_PORT==2)

	#define serXopen  	serBopen
	#define serXread  	serBread
	#define serXgetc  	serBgetc
	#define serXrdUsed  	serBrdUsed
	#define serXwrite 	serBwrite
	#define serXclose 	serBclose
	#define serXwrFlush	serBwrFlush
	#define serXrdFlush	serBrdFlush
	#define serXwrFree	serBwrFree

	#define BINBUFSIZE	INBUFSIZE
	#define BOUTBUFSIZE	OUTBUFSIZE
	
#elif (SERIAL_PORT==3)

	#define serXopen  	serCopen
	#define serXread  	serCread
	#define serXgetc  	serCgetc
	#define serXrdUsed  	serCrdUsed
	#define serXwrite 	serCwrite
	#define serXclose 	serCclose
	#define serXwrFlush	serCwrFlush
	#define serXrdFlush	serCrdFlush
	#define serXwrFree	serCwrFree

	#define CINBUFSIZE	INBUFSIZE
	#define COUTBUFSIZE	OUTBUFSIZE
	
#elif (SERIAL_PORT==4)

	#define serXopen  	serDopen
	#define serXread  	serDread
	#define serXgetc  	serDgetc
	#define serXrdUsed  	serDrdUsed
	#define serXwrite 	serDwrite
	#define serXclose 	serDclose
	#define serXwrFlush	serDwrFlush
	#define serXrdFlush	serDrdFlush
	#define serXwrFree	serDwrFree

	#define DINBUFSIZE	INBUFSIZE
	#define DOUTBUFSIZE	OUTBUFSIZE

#else
	#error "Unknown SERIAL_PORT value!"
	/* R3000 supports serial ports E and F ... */
#endif


///////////////////////////////////////////////////////////////////////  
/*
 * IMPORTANT NOTE!
 *
 * It is important to note that the TCP/IP task REQUIRES a 2K stack!
 * This is because the function resolve() (a DNS lookup) creates a
 * udp socket on the stack. If DNS lookups are NOT needed, and
 * resolve() is never called, it is possible to run the TCP/IP stack
 * in only a 1K stack.
 *
 * Also note, all TCP/IP stack functions should be call from ONE (1)
 * thread only, or data corruption and/or system instability may result!
 */

#memmap xmem
#use "ucos2.lib"
#use "dcrtcp.lib"

/* buffer for passing messages between the two tasks */
#define MSG_BUF_SIZE		256
char	message_buffer[MSG_BUF_SIZE];
int	base, boundary;

/* a semaphore for locking the above buffer */
OS_EVENT	*Semaphore;

/* buffer and count used by the transmitter */
char	send_buf[INBUFSIZE + 1], temp_buf[INBUFSIZE + 1];
int	offset;

/* our outgoing socket */
tcp_Socket s;

/*
 * The task that reads from the serial port, and stores the data in the
 * message_buffer. Ideally, most of this (reading from the serial port) should
 * be in the serial ISR, as we delay an OS level tick each time we poll for
 * new data. This is a rather serious ammount of latency between polls, and
 * could easily lead to data loss.
 */
void reader_task(void *ptr)
{
	static INT8U	error;
	auto char *	p;
	auto int 	count, temp;

	/* initilization part */
	serXopen(SERIAL_PORT_SPEED);
		
	for(;;) {
		count = serXread(temp_buf, INBUFSIZE, 0);
		if(count) {
			OSSemPend(Semaphore, 0, &error);

			p = temp_buf;
			while(count) {
				count--;

				temp = base + 1;
				if(temp == MSG_BUF_SIZE)
					temp = 0;
				if(temp == boundary) {
					/* no room; drop the character on the floor */
					continue;
				}

				message_buffer[base] = *p;
				p++;
				base = temp;
			}
			
			OSSemPost(Semaphore);

		} else {
			/* no data read - yield */
			OSTimeDly(1);
		}
	}
}

void process_data(char c)
{
	/* do extra processing on c here - nothing is done for now */

	/* queue the data for transmit */
	send_buf[offset] = c;
	offset++;
}

void send_data(void)
{
	auto int cnt;

#GLOBAL_INIT
	{
		offset = 0;
	}

	/* send the buffer */
	cnt = 0;
	while(cnt < offset) {
		cnt += sock_fastwrite(&s, send_buf + cnt, offset - cnt);
		tcp_tick((sock_type *)&s);
	}	

	/* reset */
	offset = 0;	
}

void writer_task(void *ptr)
{
	static INT8U	error;
	auto int 	temp;

	/*
	 * Listen on a socket for a connection to be made.
	 *
	 * NOTE - this should really be making a connection to a
	 * custom client program in a more active manner, but for
	 * purposes of this sample program, listening should be
	 * sufficient, and does not require a sample program to
	 * be compiled on another machine, as it only requires
	 * 'telnet'.
	 */
	tcp_listen(&s, 23, 0, 0, NULL, 0);

	/* wait for a connection to be made */
	while(!sock_established((sock_type *)&s))
		tcp_tick((sock_type *)&s);
		
	for(;;) {
		tcp_tick((sock_type *)&s);
		OSSemPend(Semaphore, 0, &error);
		while(base != boundary) {
			/* while unprocessed data exists... */

			temp = boundary + 1;
			if(temp == MSG_BUF_SIZE)
				temp = 0;

			/* as we are holding the semaphore at the moment, this should be fast */
			process_data(message_buffer[boundary]);

			boundary = temp;
		}
		OSSemPost(Semaphore);

		send_data();
	}
}

void main()
{
	auto INT8U error;

	OSInit();

	sock_init();		// init the TCP/IP stack

	// This is necessary for initializing RS232 functionality of LP35XX boards.
#if _BOARD_TYPE_ == 0x1200 || _BOARD_TYPE_ == 0x1201
	brdInit();
#endif

	/* init our buffer */
	memset(message_buffer, 0, MSG_BUF_SIZE);
	base = boundary = 0;

	/* create our semaphore */
	Semaphore = OSSemCreate(1);
	
	/* Build the two tasks */
	error = OSTaskCreate(reader_task, 	NULL, 256, 0);
	error = OSTaskCreate(writer_task,	NULL, 2048, 1); // does TCP/IP, and therefore needs the 2k stack

	/* start it all running */
	OSStart();
}
