C-Code for LANTRONIX remote control

Version V3.3:

/*	
 *	remote tool for PLAs
 *	
 *	platool.c
 *
 *	2013 Timo Milosic (t.milosic@gsi.de)
 *
 */
 
#define _POSIX_SOURCE 1
#define _GNU_SOURCE
#define _BSD_SOURCE
 
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
 
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
 
#define MAX_CHAR 0xFF
#define VERBOSE 1
 
#define SEND_LEN 7
#define RECV_TYP 7
#define RECV_LEN 24 // was 14
 
#define VERSION "0.3.1"
 
/* look-up table for baud rates */
static uint32_t const lut_baud_rates[][2] = {
	{      B0,      0 },
	{     B50,     50 },
	{     B75,     75 },
	{    B110,    110 },
	{    B134,    134 },
	{    B150,    150 },
	{    B200,    200 },
	{    B300,    300 },
	{    B600,    600 },
	{   B1200,   1200 },
	{   B1800,   1800 },
	{   B2400,   2400 },
	{   B4800,   4800 },
	{   B9600,   9600 },
	{  B19200,  19200 },
	{  B38400,  38400 },
	{  B57600,  57600 },
	{ B115200, 115200 }
};
 
/*
static const char 
	QUERY_COUNTER_VAL = (1 << 0),
	SET_COUNTER_VAL   = (1 << 1),
	QUERY_SERIAL_NUM  = (1 << 2),
	SET_SERIAL_NUM    = (1 << 3),
	TURN_ON_DISPLAY   = (1 << 4),
	QUERY_ERROR_STATE = (1 << 5),
	RESET_ERROR_STATE = (1 << 6);
*/
 
static const char 
	QUERY_COUNTER_VAL = 1,
	SET_COUNTER_VAL   = 2,
	QUERY_SERIAL_NUM  = 3,
	SET_SERIAL_NUM    = 4,
	TURN_ON_DISPLAY   = 5,
	QUERY_ERROR_STATE = 6,
	RESET_ERROR_STATE = 7,
	READ_EE_DATA      = 8,
	WRITE_EEPROM      = 9;
 
enum DEVICE { RS232, SOCKET };
enum PROTO { TCP, UDP };
 
/** forward declarations **/
 
uint32_t get_baud_rate_from_flag(speed_t flag);
 
speed_t get_speed_from_rate(uint32_t baud_rate);
 
int
	create_socket(void),
	open_port(char *port);
 
void 
	fetch_device(char *const str),
	config_serial(void),
	config_socket(void),
	socket_connect(void),
	setup_lantronix_device(
		char const *const ip,
		int port);
 
char 
	*byte_to_binary_representation(unsigned char byte),
	*byte_vec_to_bin_string(unsigned char *bytevec, size_t numbytes);
 
uint8_t
	*send_data_pattern(uint8_t *buf, size_t len),
	*receive_data_pattern(ssize_t len);
 
/** make cygwin happy **/
#ifdef __CYGWIN32__
	long random(void);
	void srandom(unsigned);
#endif
 
/** globals **/
 
struct termios *g_term_opt = NULL;
 
struct sockaddr_in *g_lantronix = NULL;
 
bool g_address_specified = false;
 
int 
	g_verbose      = 0,
	g_debug        = 0,
	g_fd_port      = 0,
	g_sock         = 0,
	g_device       = -1,
	g_network_port = -1,
	g_proto        = TCP;
 
uint16_t g_address = 0; // serial number of device
 
uint32_t
	g_turn_on_display  		= 0,
	g_query_counter    		= 0,
	g_set_counter      		= 0,
	g_query_serial     		= 0,
	g_set_serial       		= 0,
	g_query_error_state		= 0,
	g_reset_error_state		= 0,
	g_read_ee_data        = 0,
	g_read_ee_data_disp   = 0,
	g_write_eeprom        = 0,
	g_workaround_response = 0,
	g_baud_rate         	= B9600;
 
char 
	*g_serial_port	= NULL,
	*g_network_add	= NULL,
	*g_use_password = NULL,
	*g_reg_file_name = NULL;
 
uint8_t
	g_vec_send[SEND_LEN],
	g_vec_recv[RECV_LEN];
 
/** getopt support **/
 
static int c = 0;
 
struct option long_options[] = {
	{"debug",									no_argument,				NULL,	'd'},
	{"toggle-display",				no_argument,				NULL,	'D'},
	{"help",									no_argument,				NULL,	'h'},
	{"debug",									no_argument,				NULL,	'G'},	
	{"query-errors",					no_argument,				NULL,	'e'},
	{"query-counter",					no_argument,				NULL,	'c'},
	{"read-ee-data",					no_argument,				NULL,	'r'},
	{"read-ee-data-disp",			no_argument,				NULL,	'R'},
	{"query-serial",					no_argument,				NULL,	's'},
	{"verbose",								no_argument,				NULL,	'v'},
	{"version",								no_argument,				NULL,	'V'},
	{"write-eeprom",					no_argument,				NULL,	'w'},
	{"workaround-response",		no_argument,				NULL,	'W'},
	{"reset-errors",					no_argument,				NULL,	'E'},
	{"set-address",						required_argument,	NULL,	'a'},
	{"set-counter",						required_argument,	NULL,	'C'},
	{"set-serial",						required_argument,	NULL,	'S'},
	{"set-baud-rate",					required_argument,	NULL,	'b'},
	{NULL,	0,	NULL,	0}	/* required as delimiter by getopt_long() */
};
 
/* getopt()'s single character map */
const char *optstring = "a:b:cC:dDeEGhQrRS:swWvV";
 
/** function definitions **/
 
/* Cygwin >= 1.7 provides /dev/ttySx devices and cfmakeraw() macro */
#if defined(__CYGWIN__SMALLER_17)
	/* workaround for missing cfmakeraw() in Cygwin */
	void cfmakeraw(struct termios *termios_p)
	{
	    termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
	    termios_p->c_oflag &= ~OPOST;
	    termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
	    termios_p->c_cflag &= ~(CSIZE|PARENB);
	    termios_p->c_cflag |= CS8;
	}
#endif /* defined(__CYGWIN__) */
 
/* convert speed_t value to readable information */
uint32_t get_baud_rate_from_flag(speed_t flag)
{
	int i, dim = sizeof(lut_baud_rates)/sizeof(uint32_t)/2;
 
	for (i=0; i<dim; i++)
		if (flag == lut_baud_rates[i][0])
			return lut_baud_rates[i][1];
 
	return 0xFF;
}
 
/* convert integer baud rate to corresponding speed_t value */
speed_t get_speed_from_rate(uint32_t baud_rate)
{
	int i, dim = dim = sizeof(lut_baud_rates)/sizeof(uint32_t)/2;
 
	for (i=0; i<dim; i++)
		if (baud_rate == lut_baud_rates[i][1]) {
			g_baud_rate = lut_baud_rates[i][0];
			return g_baud_rate;
		}
 
	fprintf(stdout,"error: baud rate specified is invalid!\n"
								 "       using default baud rate 57600 ...\n");
 
	return -1;
}
 
/* fetch and parse device string */
void fetch_device(char *const str)
{
	char *tokpos = NULL;
 
	/* if no ':' appears a serial device is considered */
	if(NULL != (tokpos = strchr(str,':'))) { /* blindly assume this is network socket */
		g_network_port = atoi(tokpos+1);
		g_network_add = str;
		*tokpos = '\0';
		g_device = SOCKET;
	} else { /* serial port */
		g_serial_port = str;
		g_device = RS232;
	}
 
	if (g_network_add) {
		fprintf(stdout, "attempt to access remote device via network socket\n");
		fprintf(stdout, "ip address: %s\n", g_network_add);
		fprintf(stdout, "ip port:    %d\n\n", g_network_port);
	}
 
	if (g_serial_port) {
		fprintf(stdout, "attempt to access remote device via rs323\n");
		fprintf(stdout, "device: %s\n\n", g_serial_port);
	}
 
	return;
}
 
void socket_connect(void)
{
	fprintf(stdout, "attempt to establish connection ... ");
	if (connect(g_sock, (struct sockaddr *)g_lantronix,
				sizeof(struct sockaddr_in)) < 0) {
		fprintf(stdout, "failed!\n");
		perror("failed to connect with server");
		exit(-1);
	} else {
		fprintf(stdout, "success!\n");
	}
 
	return;
}
 
/* wrapper function to call setup functions for either rs232 or network socket */
void config_device(void)
{
	if (!g_network_add && !g_network_port) {
		fprintf(stdout, "error: a network device is specified by IP:PORT\n");
		exit(-1);
	}
 
	if (g_network_port && g_network_add) {
		config_socket();
		return;
	}
 
	if (g_serial_port) {
		config_serial();
		return;
	}
 
	fprintf(stdout, "error: check your device mode (rs232/ip socket)\n");
 
	return;
}
 
void setup_lantronix_device(
		char const *const ip,
		int port)
{
	g_lantronix = malloc(sizeof(struct sockaddr_in));
 
	/* construct Lantronix device sockaddr_in structure */
	memset(g_lantronix, 0, sizeof(struct sockaddr_in));
	g_lantronix->sin_family = AF_INET;
	g_lantronix->sin_addr.s_addr = inet_addr(ip);
	g_lantronix->sin_port = htons(port);
 
	fprintf(stdout, "device socket @ %s:%d\n", ip, port);
 
	return;
}
 
/* set and connect ip socket */
void config_socket(void)
{
	setup_lantronix_device(g_network_add, g_network_port);
	g_sock = create_socket();
 
	/* attempt to establish connection */
	socket_connect();
 
	return;
}
 
/* set serial port attributes */
void config_serial(void)
{
	if (!g_fd_port)
		g_fd_port = open_port(g_serial_port);
 
	g_term_opt = (struct termios *)malloc(sizeof(struct termios));
 
	/* acquire port attribute struct */
	tcgetattr(g_fd_port, g_term_opt);
 
	/* set baud rate */
	cfsetispeed(g_term_opt, g_baud_rate);
	cfsetospeed(g_term_opt, g_baud_rate);
 
	if (g_verbose) 
		fprintf(stdout, "g_baud_rate = %d\n", g_baud_rate);
 
	/* wrapper function to set  */
	cfmakeraw(g_term_opt);
 
	tcsetattr(g_fd_port, TCSANOW, g_term_opt);
 
	speed_t
		i_speed,
		o_speed;
 
	if (0xFF != (i_speed = get_baud_rate_from_flag(cfgetispeed(g_term_opt))))
		fprintf(stdout, "input speed  %u Baud\n", i_speed);
	else
		fprintf(stdout, "input speed could not be found in lut!\n"
									  "cfgetispeed() returned %u\n", cfgetispeed(g_term_opt));
 
	if (0xFF != (o_speed = get_baud_rate_from_flag(cfgetospeed(g_term_opt))))
		fprintf(stdout, "output speed %u Baud\n\n", o_speed);
	else
		fprintf(stdout, "output speed could not be found in lut!\n"
									  "cfgetospeed() returned %u\n", cfgetospeed(g_term_opt));
 
	/* we don't need struct termios anymore so let's 
	 * delete it to keep valgrind output compact */
	free(g_term_opt);
	g_term_opt = NULL;
 
	return;
}
 
/* create plain ip socket */
int create_socket(void)
{
	fprintf(stdout, "attempt to create %s socket ... ",
			g_proto == TCP ? "TCP" : "UDP");
 
	if (g_proto == TCP) {
		g_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	} else {
		g_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	}
 
	if (g_sock < 0) {
		fprintf(stdout, "failed!\n");
		perror("failed to create socket");
		exit(-1);
	} else {
		fprintf(stdout, "success!\n");
	}
 
	return g_sock;
}
 
/* attach to serial port */
int open_port(char *port)
{
	if (g_fd_port) {
		fprintf(stdout, "serial port file descriptor already exists\n");
		return g_fd_port;
	}	
 
	char err_str[256];
	int fd;
 
	if ((fd = open(port, O_RDWR|O_NOCTTY|O_NDELAY/*|O_NONBLOCK*/)) == -1) {
		snprintf(err_str, 255, "open_port(): unable to open port %s", port);
		perror(err_str);
		exit(-1);
	} else {
		fprintf(stdout, "\nsuccessfully attached to port %s ...\n\n", port);
		fcntl(fd, F_SETFL, 0);
	}
 
	return fd;
}
 
/* swap byte order of uint32_t */
void swap_bo_uint32_t(uint32_t *data)
{
	uint8_t tmp_vec[4];
	uint8_t *work_vec = (uint8_t *)data;
 
	memcpy(tmp_vec, data, sizeof(uint32_t));
 
	for (int i=0; i<4; i++)
		work_vec[i] = tmp_vec[3-i];
 
	return;
}
 
/* send data packet at 'buf' over rs232 with size 'len' */
uint8_t *send_data_pattern(
		uint8_t *buf,
		size_t len)
{
	if (g_device == RS232 && !g_fd_port)
		g_fd_port = open_port(g_serial_port);
 
	if (g_device == SOCKET && !g_sock)
		config_socket();
 
	unsigned int n;
	switch (g_device) {
		case RS232:
			n = write(g_fd_port, buf, len);
			break;
 
		case SOCKET:
			if (g_proto == TCP && g_use_password) {
				//FIXME://int passlen = strlen(g_use_password);
				int passlen = 4;
				unsigned char *newbuf = (unsigned char *)malloc((len+passlen)*sizeof(unsigned char));
				memcpy(newbuf, g_use_password,passlen);
				memcpy(newbuf+passlen, buf, len);
//				free(buf);
				buf = newbuf;
				len += passlen;
			}
			n = send(g_sock, buf, len, 0);
			break;
 
		default:
			fprintf(stdout, "error in send_data_pattern(): no valid device specified!\n");
			exit(-1);
	}
	if (n != len)
		fprintf(stdout, "error: unexpected byte write count!\n"
				"expected: %d\nread:     %d\n", (int)len, n);
 
	return g_vec_send;
}
 
/* attempt to read 'len' bytes at RS232 port */
uint8_t *receive_data_pattern(ssize_t len)
{
	fprintf(stdout, "awaiting incoming packet of size %d bytes ...\n\n", (int)len);
	ssize_t n, an = 0;
 
	switch (g_device) {
		case RS232:
			n = read(g_fd_port, g_vec_recv, len);
			break;
 
		case SOCKET:
			while(len-an != (n = recv(g_sock, &g_vec_recv[an], len-an, 0))) {
				an += n;
				printf("accumulating: socket read %zd/%zd bytes ...\n", an, len);
			}
			if (n != len) {
				an += n;
				printf("accumulating: socket read %zd/%zd bytes ... success!\n\n", an, len);
			} else {
				printf("socket read %zd bytes\n\n", len);
			}
			break;
 
		default:
			fprintf(stdout, "error in receive_data_pattern(): no valid device specified!\n");
			exit(-1);
	}
 
	return g_vec_recv; 
}
 
/* convery byte to c-string since there are no binary 
 * output formats available with printf derivatives */
char *byte_to_binary_representation(unsigned char byte)
{
	/* return buffer is allocated on the heap and 
	 * needs to be free()'ed manually */
	char *bitpat = (char *)malloc(9*sizeof(char));
 
	int i;
	unsigned char testbit = 0x1;
 
	for (i=0; i<8; i++)
		bitpat[7-i] = byte & (testbit << i) ? '1' : '0';
 
	bitpat[8] = '\0';	/* after all it's a c-string */
 
	return bitpat;
}
 
void clear_send_buf()
{
	for (int n=0; n<SEND_LEN; n++) g_vec_send[n] = 0x00;
 
	return;
}
 
void clear_recv_buf()
{
	for (int n=0; n<RECV_LEN; n++) g_vec_recv[n] = 0x00;
 
	return;
}
 
void clear_buffers()
{
	clear_send_buf();
	clear_recv_buf();
 
	return;
}
 
void send_buf(
		const char ACTION,
		uint16_t addr,
		uint32_t data)
{
	clear_buffers();
 
	// in case of "set counter value" we need to swap the byte order
	if (ACTION == SET_COUNTER_VAL) swap_bo_uint32_t(&data);
 
	// assemble command vector
	g_vec_send[0] |= ACTION;
	if (addr)	memcpy(&g_vec_send[1], &addr, sizeof(uint16_t));	
	if (data) memcpy(&g_vec_send[3], &data, sizeof(uint32_t));
 
	send_data_pattern(g_vec_send, SEND_LEN);
 
	return;
}
 
void send_recv_bufs(
		const char ACTION,
		uint16_t addr,
		uint32_t data)
{
	send_buf(ACTION, addr, data);
 
	if (g_read_ee_data) {
		receive_data_pattern(g_workaround_response ? RECV_LEN+8 : RECV_LEN);
	} else {
		receive_data_pattern(g_workaround_response ? 2*RECV_TYP : RECV_TYP);
	}
 
	if (g_workaround_response) {
		printf("\nworking around response pattern ...\n");
		printf("dropping pattern [1-8] %02X%02X%02X%02X%02X%02X%02X\n\n",
			g_vec_recv[1], g_vec_recv[2], g_vec_recv[3], 
			g_vec_recv[4], g_vec_recv[5], g_vec_recv[6], 
			g_vec_recv[7]);
		for (int n=1; n<8; n++) 
			g_vec_recv[n] = g_vec_recv[n+7];
	}
 
	return;
}
 
void recv_timeout_dummy()
{
	struct timeval tv;
 
	tv.tv_sec = 1;
	tv.tv_usec = 0;
 
	setsockopt(g_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));
	recv(g_sock, g_vec_recv, RECV_LEN, 0);
 
	return;
}
 
void query_counter_value(uint16_t addr)
{
	printf("query counter value ...\n");
	send_recv_bufs(QUERY_COUNTER_VAL, addr, 0);
 
	fprintf(stdout, "device [(%02X%02X) --> #%02X%02X] "
			"sent counter value: %02X%02X%02X%02X\n",
			g_vec_recv[1], g_vec_recv[2], g_vec_recv[2], g_vec_recv[1], 
			g_vec_recv[3], g_vec_recv[4], g_vec_recv[5], g_vec_recv[6]);
 
	return;
}
 
void set_counter_value(
		uint16_t addr,
		uint32_t val)
{
	printf("set counter value --> %X\n", val);
	send_recv_bufs(SET_COUNTER_VAL, addr, val);
 
	fprintf(stdout, "device [(%02X%02X) --> #%02X%02X] "
			"sent counter value: %02X%02X%02X%02X\n",
			g_vec_recv[1], g_vec_recv[2], g_vec_recv[2], g_vec_recv[1], 
			g_vec_recv[3], g_vec_recv[4], g_vec_recv[5], g_vec_recv[6]);
 
	return;
}
 
// MMn nicht durchdacht
void query_serial_value(uint16_t addr)
{
	printf("query serial number ...\n");
	send_recv_bufs(QUERY_SERIAL_NUM, addr, 0);
 
	fprintf(stdout, "device [(%02X%02X) --> #%02X%02X] "
			"serial number: %02X%02X%02X%02X\n",
			g_vec_recv[1], g_vec_recv[2], g_vec_recv[2], g_vec_recv[1], 
			g_vec_recv[3], g_vec_recv[4], g_vec_recv[5], g_vec_recv[6]);
 
	return;
}
 
void set_serial_value(
		uint16_t addr,
		uint32_t val)
{
	printf("set serial number --> %X\n", val);
	send_recv_bufs(SET_SERIAL_NUM, addr, val);
 
	fprintf(stdout, "device [(%02X%02X) --> #%02X%02X] "
			"respond with serial number: %02X%02X%02X%02X\n",
			g_vec_recv[1], g_vec_recv[2], g_vec_recv[2], g_vec_recv[1], 
			g_vec_recv[3], g_vec_recv[4], g_vec_recv[5], g_vec_recv[6]);
 
	return;
}
 
void turn_on_display(uint16_t addr)
{
	printf("turning on display ...\n");	
	send_buf(TURN_ON_DISPLAY, addr, 0);
 
	/* test dummy read on socket */
	recv_timeout_dummy();
 
	return;
}
 
void query_error_state(uint16_t addr)
{
	printf("query error state ...\n");
	send_recv_bufs(QUERY_ERROR_STATE, addr, 0);
 
	fprintf(stdout, "device [(%02X%02X) --> #%02X%02X] "
			"respond with error state: %02X%02X%02X%02X\n",
			g_vec_recv[1], g_vec_recv[2], g_vec_recv[2], g_vec_recv[1], 
			g_vec_recv[3], g_vec_recv[4], g_vec_recv[5], g_vec_recv[6]);
 
	return;
}
 
void reset_error_state(uint16_t addr)
{
	printf("resetting error state ...\n");
	send_buf(RESET_ERROR_STATE, addr, 0);
 
	/* test dummy read on socket */
	recv_timeout_dummy();
 
	return;
}
 
void read_ee_data(uint16_t addr)
{
	printf("query ee data ...\n");
	send_recv_bufs(READ_EE_DATA, addr, g_read_ee_data_disp ? 1 : 0);
 
	printf("listing 24 bytes:\n\n");
 
	printf("[00] %02X  ", g_vec_recv[0]);
	printf("[01] %02X  ", g_vec_recv[1]);
	printf("[02] %02X  ", g_vec_recv[2]);
	printf("[03] %02X\n", g_vec_recv[3]);
 
	printf("[04] %02X  ", g_vec_recv[4]);
	printf("[05] %02X  ", g_vec_recv[5]);
	printf("[06] %02X  ", g_vec_recv[6]);
	printf("[07] %02X\n", g_vec_recv[7]);
 
	printf("[08] %02X  ", g_vec_recv[8]);
	printf("[09] %02X  ", g_vec_recv[9]);
	printf("[10] %02X  ", g_vec_recv[10]);
	printf("[11] %02X\n", g_vec_recv[11]);
 
	printf("[12] %02X  ", g_vec_recv[12]);
	printf("[13] %02X  ", g_vec_recv[13]);
	printf("[14] %02X  ", g_vec_recv[14]);
	printf("[15] %02X\n", g_vec_recv[15]);
 
	printf("[16] %02X  ", g_vec_recv[16]);
	printf("[17] %02X  ", g_vec_recv[17]);
	printf("[18] %02X  ", g_vec_recv[18]);
	printf("[19] %02X\n", g_vec_recv[19]);
 
	printf("[20] %02X  ", g_vec_recv[20]);
	printf("[21] %02X  ", g_vec_recv[21]);
	printf("[22] %02X  ", g_vec_recv[22]);
	printf("[23] %02X\n", g_vec_recv[23]);
 
	return;
}
 
void write_eeprom(uint16_t addr)
{
	printf("dumping data to EEPROM ...\n");	
	send_buf(WRITE_EEPROM, addr, 0);
 
	/* test dummy read on socket */
	recv_timeout_dummy();
 
	return;
}
 
/* get basename by 'token' (1 char), return char pointer to string */
char *basename_t(
		char *str,
		char token)
{
	int i = 0;
	char *tmpPtr = str;
 
	while (*tmpPtr != '\0') {
		tmpPtr++;
		i++;
 
		/* this might be redundant, since bash already takes care on input */
		if (i>MAX_CHAR) {	/* MAX_CHAR limits the command line string to scan */
			fprintf(stderr,
					"Error: Command line boundary exceeded at MAX_CHAR (%d).\n"
					"This does not automatically mean that there is something wrong!\n",
					MAX_CHAR);
			exit(-1);
		}
	}
 
	while (*tmpPtr != token) {
		if (tmpPtr == str)
			return str;
		tmpPtr--;
	}
 
	return ++tmpPtr;
}
 
/* who needs a man page, actually..? */
void print_help(char *argv[])
{
	printf("Test utility for pneumatic-feed counters\n"
					"	2013 Timo Milosic\n\n");
	printf("Usage: %s [OPTION] device\n\n",basename_t(argv[0],'/'));
	printf("device port can be either a serial port or a network socket\n\n");
	printf("Options:\n\n");
	printf("  -h, --help\t\t\tdisplay this help and exit\n");
	printf("  -V, --version\t\t\tprint version\n");
	printf("  -a, --set-address=ADDR\tspecify address of terminal (HEX, eg. 1234)\n");
	printf("  -b, --set-baud-rate=RATE\tset baud rate (default 9600)\n\n");
	printf("  -W, --workaround-response\tworkaround cumbersome response pattern\n");
	printf("\t\t\t\t(LANTRONIX UDS 1100 devices with firmware revision < 6.8.x.x)\n\n");
	printf("  -c, --query-counter\t  [B1]\tquery counter value\n");
	printf("  -C, --set-counter=HEX\t  [B2]\tset counter value (HEX, eg. 12345678)\n");
	printf("  -s, --query-serial\t  [B3]\tquery serial number\n");
	printf("  -S, --set-serial=HEX\t  [B4]\tset serial number (HEX, eq. 1234)\n");
	printf("  -D, --toggle-display\t  [B5]\tturn on display\n");
	printf("  -e, --query-errors\t  [B6]\tquery error counter\n");
	printf("  -E, --reset-errors\t  [B7]\treset error counter\n");
	printf("  -r, --read-ee-data\t  [B8]\tread EEPROM data ('-R' for display output)\n");
	printf("  -w, --write-eeprom\t  [B9]\tdump data to EEPROM\n");
	printf("\n");
	printf("Examples:\n\n");
	printf(" ./%s -a ADDR -b 115200 -q /dev/ttyS0\t\tuse rs232, set it to 115200 and query the counter valus\n",basename_t(argv[0],'/'));
	printf(" ./%s -a ADDR -S SERIAL 140.181.132.120:10001\tuse TCP/IP and set serial to SERIAL (BCD)\n",basename_t(argv[0],'/'));
	printf("\n");
 
	return;
}
 
int main(int argc, char *argv[])
{
	/* parse command line with getopt_long() */
	while (1) {
		if (argc == 1) {
			print_help(argv);
			return 0;
		}
 
		int option_index = 0;
 
		c = getopt_long(argc, argv, optstring,
				long_options, &option_index);
		if (c == -1)
			break;
 
		switch (c) {
			case 0:
				printf("option %s", long_options[option_index].name);
				if (optarg)
					printf(" with arg %s", optarg);
				printf("\n");
				break;
 
			case 'a':
				g_address = strtol(optarg, NULL, 16);
				// we need this as the address can be '0' too.
				g_address_specified = true;
				break;
 
			case 'b': // set baud rate
				get_speed_from_rate(atoi(optarg));
				break;
 
			case 'c': // query counter
				g_query_counter = 1;
				break;
 
			case 'C': // set counter
				g_set_counter = strtol(optarg, NULL, 16);
				break;
 
			case 'D': // turn display on
				g_turn_on_display = 1;
				break;
 
			case 'e': // query error state
				g_query_error_state = 1;
				break;
 
			case 'E': // reset error state
				g_reset_error_state = 1;
				break;
 
			case 'G': // debug mode
				g_debug = 1;
				break;
 
			case 'h': // print help/usage
				print_help(argv);
				exit(0);
				break;
 
			case 'r': // read 24 byte stream
				g_read_ee_data = 1;
				break;
 
			case 'R': // read 24 byte stream and print on display
				g_read_ee_data = 1;
				g_read_ee_data_disp = 1;
				break;
 
			case 's': // query serial
				g_query_serial = 1;
				break;
 
			case 'S': // set serial
				g_set_serial = strtol(optarg, NULL, 16);
				break;
 
			case 'v': // verbosity
				g_verbose = 1;
				break;
 
			case 'V': // print version info and exit
				fprintf(stdout,"platool %s\n",VERSION);
				exit(0);
				break;
 
			case 'w': // write to EEPROM
				g_write_eeprom = 1;
				break;
 
			case 'W': // verbosity
				g_workaround_response = 1;
				break;
 
			case '?':
				break;
 
			default:
				printf("?? getopt returned character code 0%o ??\n", c);
		}
	}
 
	/* only one non-option element? --> rs323 port or IP address */
	int resargs = 0;
	if (((resargs = argc-optind) != 1)) {
		if (!resargs)
			fprintf(stderr,"error: no port specified!\n");
 
		if (resargs > 1)
			fprintf(stderr,"error: more than one port specified!\n");
 
		print_help(argv);
		exit(-1);
	}
 
	if (argc == 2)
		fprintf(stdout,"you have to specify an option at least ...\n");
 
	/* some commands require an address */
	if ( (g_query_counter || g_set_counter || g_query_serial || g_set_serial || 
				g_read_ee_data || g_query_error_state) && !g_address_specified ) {
		fprintf(stdout, "you have to specify an address for this command!\n");
		exit(-1);
	}
 
	/* fetch device */
	fetch_device(argv[optind]);
 
	/* config add connect the device specified */
	config_device();
 
	if (g_query_counter) query_counter_value(g_address);
	if (g_set_counter) set_counter_value(g_address, g_set_counter);
	if (g_query_serial) query_serial_value(g_address);
	if (g_set_serial) set_serial_value(g_address, g_set_serial);
	if (g_turn_on_display) turn_on_display(g_address);
	if (g_query_error_state) query_error_state(g_address);
	if (g_reset_error_state) reset_error_state(g_address);
	if (g_read_ee_data) read_ee_data(g_address);
	if (g_write_eeprom) write_eeprom(g_address);
 
	/* close socket if available */
	if (g_sock) close(g_sock);
 
	return 0;
}