/* Copyright 2011, Unpublished Work of Technologic Systems
 * All Rights Reserved.
 *
 * THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL,
 * PROPRIETARY AND TRADE SECRET INFORMATION OF TECHNOLOGIC SYSTEMS.
 * ACCESS TO THIS WORK IS RESTRICTED TO (I) TECHNOLOGIC SYSTEMS 
 * EMPLOYEES WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE
 * OF THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN TECHNOLOGIC
 * SYSTEMS WHO HAVE ENTERED INTO APPROPRIATE LICENSE AGREEMENTS.  NO
 * PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED, COPIED, 
 * DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED, CONDENSED, 
 * EXPANDED, COLLECTED, COMPILED, LINKED, RECAST, TRANSFORMED, ADAPTED
 * IN ANY FORM OR BY ANY MEANS, MANUAL, MECHANICAL, CHEMICAL, 
 * ELECTRICAL, ELECTRONIC, OPTICAL, BIOLOGICAL, OR OTHERWISE WITHOUT
 * THE PRIOR WRITTEN PERMISSION AND CONSENT OF TECHNOLOGIC SYSTEMS.
 * ANY USE OR EXPLOITATION OF THIS WORK WITHOUT THE PRIOR WRITTEN
 * CONSENT OF TECHNOLOGIC SYSTEMS COULD SUBJECT THE PERPETRATOR TO
 * CRIMINAL AND CIVIL LIABILITY.
 */

/* To compile the libdaqctl testbench sample code, use the appropriate
 * cross compiler and run the command:
 *
 *   gcc -Wall -O -mcpu=arm9 -DTESTBENCH -o libxuartctl-tb libxuartctl.c
 *
 * On uclibc based initrd's, the following additional gcc options are
 * necessary: -Wl,--rpath,/slib -Wl,-dynamic-linker,/slib/ld-uClibc.so.0
 */

#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sched.h>
#include <getopt.h>
#include <termios.h>
#include <ctype.h>
#include <limits.h>

#ifndef TEMP_FAILURE_RETRY
# define TEMP_FAILURE_RETRY(expression) \
  (__extension__                                                              \
    ({ long int __result;                                                     \
       do __result = (long int) (expression);                                 \
       while (__result == -1L && errno == EINTR);                             \
       __result; }))
#endif

/* This function is used to connect to a socket and get a socket pointer in 
 * return.  It takes two arguments, hostspec and args.  The hostspec must be
 * in the form of hostname:port, if it is NULL it will default to
 * localhost:7350 (the first XUART port).  The args argument is any additional
 * arguments.  In the case of this library, it is required to use at least
 * --mode=raw since the read/write functions provided by this library utilize
 * the raw mode of the XUARTs.  All other options will default to the xuartctl
 * server defaults.
 *
 * This function will return a socket buffer FD
 */

int xuart_connect(char *hostspec, char *args) {
	struct sockaddr_in sa;
	struct hostent *he;
	int sk, r, x, tos = IPTOS_LOWDELAY;
	char *p;
	char buf[1024];
	char cmd[1024];
	struct linger lx;

	/* hostspec must be in form hostname:port */
	if (hostspec) strncpy(buf, hostspec, sizeof(buf));
	else strcpy(buf, "127.0.0.1");
	buf[sizeof(buf) - 1] = 0;

	if (args) {
		snprintf(cmd, sizeof(cmd),
		  "xuartctl --server --port=%s %s >/dev/null 2>&1", buf, args);
		system(cmd);
	}
	p = buf;
	while (*p != ':' && *p != 0) p++;
	if (*p == 0) p = "7350";
	else {
		*p = 0;
		p++;
	}

	sa.sin_family = AF_INET;
	he = gethostbyname(buf);
	assert(he != NULL);
	memcpy(&sa.sin_addr, he->h_addr_list[0], he->h_length);
	sa.sin_port = htons(strtoul(p, NULL, 0));
	assert(sa.sin_port != 0);
	sk = socket(PF_INET, SOCK_STREAM, 0);
	assert(sk != -1);
	lx.l_onoff = 1;
	lx.l_linger = 15;
	setsockopt(sk, SOL_SOCKET, SO_LINGER, &lx, sizeof(lx));
	r = connect(sk, (struct sockaddr *)&sa, sizeof(sa));
	setsockopt(sk, IPPROTO_IP, IP_TOS, &tos, 4);
	x = 1;
	setsockopt(sk, IPPROTO_TCP, TCP_NODELAY, &x, 4);

	if (r) {
		close(sk);
		return -1;
	} else return sk;
}

/* This function will do a raw read (16 bit) on a previously opened XUART
 * socket.  It takes 4 arguments, socket FD, buffer, count, and how many bits
 * the data is.  It will throw out any non-characters received on the line. 
 *
 * The socket FD needs to be the same socket returned from a call to 
 * xuart_connect.
 * The buffer is a void *, but 16 bits at a time will be read from the socket
 * and placed in this buffer.  This function will place in *buf the data
 * requested that is nbits wide, right aligned.
 * The size is the number of bytes at most to read from socket sk.
 * The nbits is how many bits wide the data portion is
 *
 * This function returns the number of bytes read
 */
size_t xuart_rawread(int sk, void *buf, size_t count, int nbits) {
	unsigned short *get, *put; 
	size_t nread, x, num;
	unsigned short shift = (11 % nbits), clr = (0xffff << nbits);

	nread = read(sk, buf, count);

	get = buf;
	put = buf;

	for(x = 0; x < nread; x += 2) {
		if(0x0 == (*get & (0x1800))) {
			*put = *get;
			*put = *put >> shift;
			*put &= ~clr;
			put++;
		}
		get++;
	}
	return ((unsigned char) put - (unsigned char) buf);
}

/* This function will do a raw write (16 bit) on a previously opened XUART
 * socket.  It takes 4 arguments, socket FD, buffer, count, and how many bits
 * the data is.  
 *
 * The socket FD needs to be the same socket returned from a call to 
 * xuart_connect.
 * The buffer is a void *, but 16 bits at a time will be written to the socket
 * so the data being sent must occupy the lower nbits of the 16bit wide *buf.
 * The size is the number of bytes at most to write to socket sk.
 * The nbits is how many bits wide the data portion is
 *
 * This function returns the number of bytes written 
 */
size_t xuart_rawwrite(int sk, void *buf, size_t count, int nbits) {
	unsigned short tempbuf[65536];
	unsigned short *p;
	size_t x, written;
	unsigned short clr = (0xffff << nbits);
	

	p = buf;
	for(x = 0; x < count && x < sizeof(tempbuf); x += 2) {
		*p &= ~clr;
		tempbuf[x/2] = *p;
		p++;
	}
	
	written = write(sk, tempbuf, x);
	return written;
}

/* This function will issue a break for bit_times number of bit times on a 
 * previously opened XUART socket.  It takes three arguments, socket FD, number
 * of bit times to break for, and whether this number should be shifted by 6 or
 * left as-is.  The shift is to attain greater numbers than a 10bit number can
 * normally attain.
 *
 * The socket FD needs to he a socket returned from a call to xuart_connect.
 * The bit_times argument is a 10bit number that specifies how many bit times
 *   to break for.  If the number specified is more than 10bits it is truncated.
 * The shift_by_6 argument is to tell the XUART state machine to shift the 10bit
 *  bit_times forward by 6, to allow for much larger bit_times.
 *
 * This function returns the number of bytes written, which should always be 2
 *
 * XXX: It is recommended to have an idlewrite following a breakwrite
 */
size_t xuart_breakwrite(int sk, unsigned int bit_times, int shift_by_6) {
	unsigned short buf = 0;	
	
	if(bit_times > 0x3ff) bit_times = 0x3ff;
	buf |= bit_times;
	buf |= shift_by_6 << 10;
	buf |= 0x01 << 11;

	return write(sk, &buf, 2);
}

/* This function will read breaks from the line and return how many breaks
 * were received throwing out all other characters.  It takes two arguments,
 * the socket FD, and the number of characters to read().  A successful break
 * will return one break, no matter the length.
 *
 * The socket FD nees to be a socket returned from xuart_connect.
 * Count is the number of bytes to read() from the fifo.
 *
 * This function will return the number of breaks received, NOT the number of
 * characters read from the fife by read().
 */
size_t xuart_breakread(int sk, size_t count) {
	unsigned short buf[1024], *get;
	int numbreak = 0, x;
	size_t nread;
	
	if(count < sizeof(buf)) count = (sizeof(buf));

	nread = read(sk, buf, count);

	get = buf;
	
	for(x = 0; x < nread; x += 2) {
		if(0x0 == (*get & (0x1000))) numbreak++;
		get++;
	}

	return numbreak;
}

/* This function will issue an idle for bit_times number of bit times on a 
 * previously opened XUART socket.  It takes three arguments, socket FD, number
 * of bit times to idle for, and whether this number should be shifted by 6 or
 * left as-is.  The shift is to attain greater numbers than a 10bit number can
 * normally attain.
 *
 * The socket FD needs to he a socket returned from a call to xuart_connect.
 * The bit_times argument is a 10bit number that specifies how many bit times
 *   to break for.  If the number specified is more than 10bits it is truncated.
 * The shift_by_6 argument is to tell the XUART state machine to shift the 10bit
 *  bit_times forward by 6, to allow for much larger bit_times.
 *
 * This function returns the number of bytes written, which should always be 2
 */
size_t xuart_idlewrite(int sk, unsigned int bit_times, int shift_by_6) {
	unsigned short buf = 0;	
	
	if(bit_times > 0x3ff) bit_times = 0x3ff;
	buf |= bit_times;
	buf |= shift_by_6 << 10;
	buf |= 0x02 << 11;

	return write(sk, &buf, 2);
}



#ifdef TESTBENCH
/* Sample client app */
/* This sample code will simply connect to XUART#1, 9n0, raw mode, at 9600 baud
 * After it gets a socket FD, it will break for a 11 bit times, write out the 
 * first array entry, break and then idle again, read it back, then break and
 * idle again.  It will read back how many breaks have occured and print that 
 * it received them.  If no loopback is connected, this code will run forever.
 * Once that is complete it will compre the received array and the sent array.
 */ 

int main(void) {
	int sk, n, x, loop;
	unsigned short recvbuf[4];
	unsigned short samplebuf[4] = 
		{0x0055,0x00aa,0x0155,0x01aa};
	

	sk = xuart_connect("127.0.0.1:7350", "--mode=9n1,raw --speed=9600");
	assert(sk != -1);

	for (x = 0; x < 4; x++) {
		n = 0;
		xuart_breakwrite(sk, 11, 0);
		xuart_idlewrite(sk, 11, 0);
		while(0 == n) {
			n = TEMP_FAILURE_RETRY(xuart_rawwrite(sk, samplebuf+x,
			  2, 9));
		}
		xuart_breakwrite(sk, 11, 0);
		xuart_breakwrite(sk, 11, 0);
		xuart_idlewrite(sk, 11, 0);
		n = 0;
		while(0 == n) {
			n = TEMP_FAILURE_RETRY(xuart_rawread(sk, recvbuf+x,
			  2, 9));
		}
		xuart_breakwrite(sk, 11, 0);
		xuart_breakwrite(sk, 11, 0);
		xuart_idlewrite(sk, 11, 0);
		xuart_idlewrite(sk, 11, 0);
		n = 0;
		while(0 == n) {
			n = TEMP_FAILURE_RETRY(xuart_breakread(sk, 1024));
		}
		if(n) printf("Received %d breaks\n", n);
		

	}

	for(x = 0; x < 4; x++) {
		if(recvbuf[x] != samplebuf[x]) {
			printf("Failed loopback!\n");
			printf(" got: %04X,%04X,%04X,%04X\n",
			  recvbuf[0], recvbuf[1], recvbuf[2], recvbuf[3]); 
			printf("want: %04X,%04X,%04X,%04X\n",
			  samplebuf[0], samplebuf[1], samplebuf[2],
			  samplebuf[3]); 
			goto FAILED;
		}
	}
	printf("Success!\n");

FAILED:
	close(sk);
	return 0;
}
#endif
