
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <termios.h>
#include <string.h>

typedef struct {
	unsigned char function;
	unsigned char nobytes;
	unsigned char *data;
} upfb_request_t;

typedef struct {
	unsigned char nobytes;
	unsigned char *data;
} upfb_reply_t;

typedef struct {
	char *name;
	upfb_request_t request;
	upfb_reply_t reply;
} upfb_cmd_t;

upfb_cmd_t upfb_cmds[] = {
	{ "initialize",       
	  { 0x00, }, 
	  { 1, (unsigned char []){ 0x00 } } },

	{ "get_status",       
	  { 0x01, }, 
	  { 1, (unsigned char []){ 0x00 } } },

	{ "reset",            
	  { 0x02, }, 
	  { 1, (unsigned char []){ 0x00 } } },

	{ "enable_12v",       
	  { 0x03, 1, (unsigned char []){ 0x00 } }, 
	  { 1, (unsigned char []){ 0x00 } } },

	{ "disable_12v",      
	  { 0x03, 1, (unsigned char []){ 0xff } },
	  { 1, (unsigned char []){ 0x00 } } },

	{ "enable_vpp",       
	  { 0x04, 1, (unsigned char []){ 0x00 } },
	  { 1, (unsigned char []){ 0x00 } } },

	{ "disable_vpp",      
	  { 0x04, 1, (unsigned char []){ 0xff } },
	  { 1, (unsigned char []){ 0x00 } } },

	{ "enable_mdx",       
	  { 0x05, 1, (unsigned char []){ 0x00 } },
	  { 1, (unsigned char []){ 0x00 } } },

	{ "disable_mdx",      
	  { 0x05, 1, (unsigned char []){ 0xff } },
	  { 1, (unsigned char []){ 0x00 } } },

	{ "transparent",      
	  { 0x08, }, 
	  { 1, (unsigned char []){ 0x00 } } },

	{ "setup_sci_host_9600", 
	  { 0x09, 5, (unsigned char []){ 0x00, 0x03, 0x01, 0x00, 0x00 } },
	    { 2, (unsigned char []){ 0x02, 0x55 } } },

	{ "setup_sci_host_115200", 
	  { 0x09, 5, (unsigned char []){ 0x00, 0x07, 0x01, 0x00, 0x00 } },
	    { 2, (unsigned char []){ 0x02, 0x55 } } },

	{ "setup_sci_fztat_9600", 
	  { 0x09, 5, (unsigned char []){ 0x01, 0x03, 0x01, 0x00, 0x00 } },
	    { 2, (unsigned char []){ 0x01, 0x56 } } },

	{ "setup_sci_fztat_115200", 
	  { 0x09, 5, (unsigned char []){ 0x01, 0x07, 0x01, 0x00, 0x00 } },
	    { 2, (unsigned char []){ 0x01, 0x56 } } },

	{ "reset_on",            
	  { 0x0e, }, 
	  { 1, (unsigned char []){ 0x00 } } },

	{ "reset_off",            
	  { 0x0f, }, 
	  { 1, (unsigned char []){ 0x00 } } },
};

#define UFPB_CMD_INIT 0

int tty_setup(int fd, int speed)
{
    struct termios ios;

    if (!isatty(fd)) {
	    fprintf(stderr, "not a tty\n");
	    return -1;
    }

    memset(&ios, 0, sizeof(ios));

    if (tcgetattr(fd, &ios) != 0) {
	perror("tcgetattr");
	return -1;
    }

//    ios.c_cflag = CREAD | CLOCAL | CS8;

    fprintf(stderr, "setting bps to %u\n", speed);

    switch(speed) {
    case 9600: speed = B9600; break;
    case 19200: speed = B19200; break;
    case 38400: speed = B38400; break;
    case 57600: speed = B57600; break;
    case 115200: speed = B115200; break;
    default:
	    fprintf(stderr, "unsupported bps speed\n");
	    return -1;
    }


    if (cfsetospeed(&ios, speed) != 0) {
	    perror("cfsetospeed");
	    return -1;
    }

    if (cfsetispeed(&ios, speed) != 0) {
	    perror("cfsetispeed");
	    return -1;
    }

    if (tcsetattr(fd, TCSADRAIN, &ios) != 0) {
	perror("tcsetattr");
	return -1;
    }

    return 0;
}

int ufpb_send(int fd, unsigned char data)
{
	if (write(fd, &data, 1) != 1) {
		return -1;
	}

	return 0;
}

int ufpb_recv(int fd, unsigned char *buf)
{
	if (read(fd, buf, 1) != 1) {
		return -1;
	}

	return 0;
}

int ufpb_request_write(int fd, int request, unsigned char *buf, int nobytes)
{
	int k;

	ufpb_send(fd, 0xaa);
	ufpb_send(fd, request);
	ufpb_send(fd, nobytes);
	
	for (k = 0; k < nobytes; k++) {
		ufpb_send(fd, buf[k]);
	}
	
	ufpb_send(fd, 0x55);

	return 0;
}

int ufpb_request_read(int fd, int request, unsigned char *buf, int maxbytes)
{
	int k, n, bad = 0;
	unsigned char data;

	ufpb_recv(fd, &data);

	if (data != (0xe0 | request)) {
		bad = 1;
	}

	ufpb_recv(fd, &data);

	n = data;

	if (n > maxbytes) {
		n = maxbytes;
	}

	for (k = 0; k < n; k++) {
		ufpb_recv(fd, &buf[k]);
	}
	
	ufpb_recv(fd, &data);

	if (data != 0x00) {
		bad = 1;
	}

	if (bad) {
		return -1;
	}

	return n;
}

int main(void)
{
	int fd, k;
	unsigned char buf[2];

	fd = open("/dev/ttyS0", O_RDWR);

	if (fd < 0) {
		perror("open");
		return 1;
	}

	if (tty_setup(fd, 9600) != 0) {
		close(fd);
		return 1;
	}

	for (k = 0; k < 0x10000; k++) {
		buf[0] = k >> 8;
		buf[1] = k & 0xff;

		fprintf(stderr, ".");

		if (ufpb_request_write(fd, 0x0a, buf, 2) < 0) {
			break;
		}

		if (ufpb_request_read(fd, 0x0a, buf, 1) < 0) {
			break;
		}
		write(1, buf, 1);
	}

	close(fd);

	return 0;
}


