/*  Copyright 2010, 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.
 */

#include <assert.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/socket.h>
#include <sys/resource.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 <linux/unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <time.h>
#include <limits.h>
#include <sched.h>
#include <ctype.h>
#include <signal.h>
#include "ts.h"
#include "sock.h"
#include "peekpoke.h"
#include "canctl.h"
#include "opt.h"

#define	PORT		7552
TS_CPU cpu;

int *baudparms;
void (*buslock)(void);
void (*busunlock)(void);
void (*buspreempt)(void);
unsigned int (*can_peek8)(int adr);
void (*can_poke8)(int adr, unsigned int dat);
void (*can_peekstream)(int adr, unsigned char *buf, int n);
void (*can_pokestream)(int adr, unsigned char *buf, int n);
int irq_num;

struct instance_data {
  int desc_type; // 0=socket, 1=CAN IRQ
  // filter consists of an id, which indicates the value we want to match,
  // and a mask which indicates which bits in the id must match
  unsigned id, mask, flags;
};
struct server_data {
};

int server_service(SC *conn,void *data,SC *head);
SC *new_server_conn(int s);

typedef unsigned char uint8;
typedef unsigned uint32;

#define CAN_MSG_LENGTH 8
#define FLAG_BUS_ERROR 1
#define FLAG_ERROR_PASSIVE 2
#define FLAG_DATA_OVERRUN 4
#define FLAG_ERROR_WARNING 8
#define FLAG_RTR 0x10
#define FLAG_EXT_ID 0x20
#define FLAG_LOCAL 0x40
#define FLAG_CONTROL 0x80
#define FLAG_CMD_RXEN 1
/*
  Raw Packet Format:

  UINT8    flags:
	       control information present (reserved for future use)
	       message originates from this node (unused)
	       message has extended ID
	       remote transmission request (RTR)
	       error warning
	       data overrun
	       error passive 
               bus error
  UINT32   id
  UINT32   timestamp_seconds
  UINT32   timestamp_microseconds
  UINT8    bytes
  UINT8[8] data
 */
struct canmsg{
  uint32 flags;  // flags
  uint32 id;
  struct timeval timestamp;
  uint32 length;
  uint8  data[CAN_MSG_LENGTH];  
};
typedef struct canmsg canmsg;

inline void canmsg_init(canmsg *msg) {
  memset(msg,0,sizeof(*msg));
}

uint32 ntoh_UINT32_LE(uint8 *str) {
  return (((uint32)str[0])<<0)
    + (((uint32)str[1])<<8)
    + (((uint32)str[2])<<16)
    + (((uint32)str[3])<<24);
}

void hton_UINT32_LE(uint32 i,uint8 *str) {
  str[0] = ((i >> 0) & 0xFF);
  str[1] = ((i >> 8) & 0xFF);
  str[2] = ((i >> 16) & 0xFF);
  str[3] = ((i >> 24) & 0xFF);
}

void ntoh_canmsg(uint8 *pkt,struct canmsg *msg) {
  int i;

  msg->flags = (uint32)(pkt[0]);
  msg->id = ntoh_UINT32_LE(pkt+1);
  msg->timestamp.tv_sec = ntoh_UINT32_LE(pkt+5);
  msg->timestamp.tv_usec = ntoh_UINT32_LE(pkt+9);
  msg->length = (uint32)(pkt[13]);
  for (i=0;i<8;i++) {
    msg->data[i] = pkt[14+i];
  }
}

void hton_canmsg(struct canmsg *msg,uint8 *pkt) {
  int i;

  pkt[0] = (uint8)msg->flags;
  hton_UINT32_LE(msg->id,pkt+1);
  hton_UINT32_LE(msg->timestamp.tv_sec,pkt+5);
  hton_UINT32_LE(msg->timestamp.tv_usec,pkt+9);
  pkt[13] = (uint8)msg->length;
  for (i=0;i<8;i++) {
    pkt[14+i] = msg->data[i];
  }
}

inline void canmsg_init_cmd0(canmsg *msg,unsigned id,unsigned mask) {
  canmsg_init(msg);
  msg->flags = FLAG_CONTROL;
  msg->length = 0;
  hton_UINT32_LE(id,msg->data);
  hton_UINT32_LE(mask,msg->data+4);
}



#define CAN_RTR	0x10
static void can_loadtx(unsigned int id, unsigned char *d, int flags) {
	unsigned char buf[13];
	unsigned char *b = buf;
	int dlc = flags & 0xf;
	int i;

	assert(dlc <= 8);

	if (flags & CAN_RTR) *b = 0xc0; else *b = 0x80;
	*b++ |= dlc;
	id <<= 3;
	*b++ = (id >> 24);
	*b++ = (id >> 16);
	*b++ = (id >> 8);
	*b++ = (id & 0xff);
	while (dlc--) *b++ = *d++;

	can_pokestream(16, buf, b - buf);
}

static int can_irqfd=-1;
static int can_irqwait_noack(int poll) {
  fd_set rfd;
  struct timeval tv;
  int i;

  tv.tv_sec = tv.tv_usec = 0;
  FD_ZERO(&rfd);
  FD_SET(can_irqfd,&rfd);
  do {
    i = select(can_irqfd+1,&rfd, NULL, NULL, poll ? &tv : NULL);
  } while (i == -1 && errno == EINTR);
  return i > 0;
}

static void can_irqwait(void) {
  int dummy;

  assert(can_irqfd != -1);
  read(can_irqfd, &dummy, sizeof(dummy));
}

void init_baud(int opt_baud,int *opt_btr0,int *opt_btr1) {
  int i;
  
  if (!baudparms) return;
  if (opt_baud < baudparms[0]) {
    fprintf(stderr,"%d < %d, using %d baud\n",opt_baud,baudparms[0],baudparms[0]);
    opt_baud = baudparms[0];
  } else if (opt_baud > 1000000) {
    fprintf(stderr,"%d > 1000000, using 1000000 baud\n",opt_baud);
    opt_baud = 1000000;
  } else {
    i = 0;
    while (baudparms[i]) {
      if (baudparms[i] >= opt_baud) {
	break;
      }
      i += 3;
    }
    if (i > 0) {
      if (baudparms[i] - opt_baud > opt_baud - baudparms[i-3]) {
	i -= 3; // pick a slightly slower baud rate if it is closer to target
      }
    }
    // assert(baudparms[i]);
    opt_baud = baudparms[i];
    *opt_btr0 = baudparms[i+1];
    *opt_btr1 = baudparms[i+2];
    fprintf(stderr,"using %d baud\n",opt_baud);
  }
}

void init_can(int opt_btr0,int opt_btr1) {
  buslock();
  can_poke8(0, 0x1); /* Enter reset mode */
  can_poke8(31, 0x80); /* Enable PeliCAN mode */
  can_poke8(4, 0xff); /* All interrupts enabled */
  can_poke8(6, opt_btr0); /* BTR0 , default is for 500kbps */
  can_poke8(7, opt_btr1); /* BTR1 , default is for 500kbps */
  can_poke8(13, 96); /* Error warning limit */
  can_poke8(14, 0); /* Reset RX error counter to 0 */
  can_poke8(15, 0); /* Reset TX error counter to 0 */
  
  can_poke8(20, 0xff); /* Acceptance mask disabled (all packets rx'ed) */
  can_poke8(21, 0xff);
  can_poke8(22, 0xff);
  can_poke8(23, 0xff);
  
  can_peek8(3); /* Clear pending interrupts */
  
  can_poke8(0, 0); /* Enter operational mode */
  busunlock();
}

int open_irq() {
  char irqfile[32];
  if (irq_num < 0) return -1;
  sprintf(irqfile,"/proc/irq/%d/irq",irq_num);
  can_irqfd = open(irqfile, O_RDONLY|O_SYNC);
  return can_irqfd;
}

 /*
 * This function records a PID so that ts7500ctl --loadfpga will send
 * a SIGHUP to the PID after FPGA reconfiguration.
 *
 * Predefined slots:
 *  0 - sdctl NBD service
 *  1 - nand NBD service
 *  2 - SPI flash NBD service
 *  3 - xuartctl --server service
 *  4 - canctl --server service
 *  5 - dmxctl --server service
 *  6 - spictl --server service
 *  7 - reserved
 *  8 - reserved
 *  >= 9 is invalid
 *
 * Before calling, application should setup or ignore the SIGHUP signal. 
 * e.g. signal(SIGHUP, SIG_IGN);
 */
record_daemon_pid(int slot) {
  key_t shmkey;
  int shmid;
  unsigned int *sbus_shm;
  
  shmkey = 0x75000000;
  shmid = shmget(shmkey, 0x1000, IPC_CREAT);
  assert(shmid != -1);
  sbus_shm = shmat(shmid, NULL, 0);
  sbus_shm += (slot * 32);
  sbus_shm[0] = getpid();

  shmdt(sbus_shm);
  return 0;
}

int gotHUP = 0;

void do_hup() {
  gotHUP = 1;
}

int do_server(int opt_btr0,int opt_btr1) {
  int s;
  struct server_data sd;
  struct instance_data *idata;
  SC *irqd;

  struct sigaction sa;

  sa.sa_handler = do_hup;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction(SIGHUP, &sa, NULL);

  if (!irq_num) return -1;
  signal(SIGPIPE,SIG_IGN);
  s = create_server_socket(PORT);
  if (s < 0) {
    perror("create_server_socket:");
    return 3;
  }
  
  // create CAN IRQ descriptor
  idata = malloc(sizeof(struct instance_data));
  idata->desc_type = 1;
  irqd = malloc(sizeof(SC));
  assert((irqd->s = open_irq())>0);
  assert(irqd->s != -1);
  init_can(opt_btr0,opt_btr1);
  irqd->buf = malloc(4);
  irqd->len = 4;
  irqd->used = 0;
  irqd->want = 4;
  irqd->inst_data = idata;
  irqd->next = 0;
  irqd->wbuf = malloc(4);
  irqd->wlen = irqd->wwr = 0;
  irqd->wfd = malloc(sizeof(fd_set));
  daemon(1,0);
  record_daemon_pid(4);

  return run_multi_server(s,server_service,new_server_conn,&sd,irqd);
}

void canmsg_init_tx(canmsg *msg,uint32 id,int flags,int length,unsigned char *data) {
  canmsg_init(msg);
  msg->flags = flags;
  msg->id = id;
  gettimeofday(&msg->timestamp,NULL);
  msg->length = length;
  memcpy(msg->data,data,length);

}

int can_tx_remote(int s,canmsg *msg) {
  int i;

  i = write(s,msg,sizeof(canmsg));
  return (i != sizeof(canmsg));
}

static int rx_int_in_tx = 0;
int can_tx_local(canmsg *msg) {
  int j,flags=0;

  if (msg->flags & FLAG_CONTROL) return 0;
  if (msg->flags & FLAG_RTR) flags = CAN_RTR;
  flags += msg->length;
  buslock();
  can_loadtx(msg->id, msg->data, flags);
  can_poke8(1, 0x1); /* TX command */
  do {
    busunlock();
    can_irqwait();
    buslock();
    //    j = can_peek8(3); /* pending interrupts */
    //  } while ((j & 0x2) == 0); /* wait til TX complete */
    j = can_peek8(2); // Poll status
    can_peek8(3); // Clear pending interrupts
  } while ((j & 0x44) == 0);
  /*
    register 2: status register
    0x04 : buffer released
    0x40 : tx in error status
    register 3: interrupt reguster
    0x02 : transmit interrupt
    0x01 : receive interrupt
   */
  if ((j & 0x40) && can_peek8(15) >= 96) {
    can_poke8(1, 0x2); /* Abort transmission */
    j = can_peek8(12); /* error capture reg */
    can_poke8(0, 1); /* Enter reset mode */
    busunlock();
    printf("txerror=0x%x\n", j);
    return 0;
  }

  busunlock();
  return 1;
}

int can_tx_local_errorq() {
  int j,ret = 0;
  buslock();
  j = can_peek8(2); // Poll status
  if ((j & 0x40) && can_peek8(15) >= 96) {
    can_poke8(1, 0x2); /* Abort transmission */
    ret = 0x80000000+can_peek8(12); /* error capture reg */
    can_poke8(0, 1); /* Enter reset mode */
  }
  busunlock();
  return ret;
}

int can_tx_local_nonblocking(canmsg *msg) {
  int j,flags=0;

  if (msg->flags & FLAG_CONTROL) return 0;
  if (msg->flags & FLAG_RTR) flags = CAN_RTR;
  flags += msg->length;
  buslock();
  j = can_peek8(2); // Poll status
  if ((j & 0x04) == 0) {
    busunlock();
    return 0; // not ready to send yet
  }
  can_loadtx(msg->id, msg->data, flags);
  can_poke8(1, 0x1); /* TX command */
  busunlock();
  return 1;
}

int can_rx_local(canmsg *msg) {
  unsigned char d[16];
  int i,j,n,ret=0;

  canmsg_init(msg);
  buslock();
  j = can_peek8(3); /* read IRQ */
  if (j & 0x80) msg->flags |= FLAG_BUS_ERROR;
  if (j & 0x20) msg->flags |= FLAG_ERROR_PASSIVE;
  if (j & 0x8) msg->flags |= FLAG_DATA_OVERRUN;
  if (j & 0x4) msg->flags |= FLAG_ERROR_WARNING;
  n = can_peek8(29);
  ret = ((j & 0xAD) != 0) || (n>0);
  if (n > 128) n &= 0x7F;
  if (n) { // j & 0x1
    unsigned short t;
    unsigned len;

    can_peekstream(16,(unsigned char *)&t,2);
    //t = (t << 8) + (t >> 8); ???
    msg->length = t & 0x0F;
    if (msg->length > 8) msg->length = 8;
    if (t & 0x80) msg->flags |= FLAG_EXT_ID;
    if (t & 0x40) msg->flags |= FLAG_RTR;

    d[0] = t & 0xff; // flags+len; 0x80=EXT,0x40=RTR
    d[1] = t >> 8;   // ??
    /* standard or extended frame */
    if (d[0] & 0x80) len = 3; else len = 1;
    
    if (!(d[0] & 0x40)) /* Not RTR packet? */
      len += ((d[0] & 0xf) > 8) ? 8 : (d[0] & 0xf);
      
    can_peekstream(18, &d[2], len); // data
    
    if (t & 0x80) {
      msg->id = d[1] << 21;
      msg->id |= d[2] << 13;
      msg->id |= d[3] << 5;
      msg->id |= d[4] >> 3;
      for (i=0;i<8;i++) msg->data[i] = d[i+5];
    } else {
      msg->id = d[1] << 21;
      msg->id |= (d[2] & 0xe0) << 13;
      msg->id = msg->id >> 18;
      for (i=0;i<8;i++) msg->data[i] = d[i+3];
     }

    can_poke8(1, 0x4); /* Release receive buffer */
  }
  if (j & 0x8) can_poke8(1, 0x8); /* clear data overrun */
  busunlock();
  return ret;
}

int can_rx_remote(int s,struct canmsg *msg) {
  int bytes;

  bytes = read(s,msg,sizeof(canmsg));
  return (bytes == sizeof(canmsg));
}

void print_canmsg(canmsg *msg) {
  struct tm * tm;
  int i;

  if (msg->flags & FLAG_CONTROL) printf("*** control packet\n");
  if (msg->flags & FLAG_BUS_ERROR) printf("*** bus error\n");
  if (msg->flags & FLAG_ERROR_PASSIVE) printf("*** error passive\n");
  if (msg->flags & FLAG_DATA_OVERRUN) printf("*** data overrun error\n");
  if (msg->flags & FLAG_ERROR_WARNING) printf("*** error warning\n");
  tm = localtime(&msg->timestamp.tv_sec);
  printf("%02d:%02d:%02d.%06d %c%c ", 
	 tm->tm_hour, tm->tm_min, tm->tm_sec,msg->timestamp.tv_usec, 
	 (msg->flags & FLAG_EXT_ID) ? 'E' : 'S', 
	 (msg->flags & FLAG_RTR)     ? 'R' : '.');
  if (msg->flags & FLAG_CONTROL) {
    printf("id=0x%08x ", msg->id);
    printf("cmd=%d ",msg->length);
    printf("dat=");
    for (i = 0; i < 8; i++) {
      printf("0x%02x", msg->data[i]);
      if (i != (msg->length - 1)) printf(":");
    }
  } else if (msg->length != 0) {
    printf("id=0x%08x ", msg->id);
    printf("dat=");
    for (i = 0; i < msg->length; i++) {
      printf("0x%02x", msg->data[i]);
      if (i != (msg->length - 1)) printf(":");
    }
  }
  printf("\n");
}

void can_rx_dump(int s) {
  canmsg msg;

  if (s >= 0) {
    canmsg_init_cmd0(&msg,0,0); // Rx all packets
    msg.flags |= FLAG_CMD_RXEN;
    can_tx_remote(s,&msg);
  }

  for(;;) {
    if (s < 0) {
      can_irqwait();
      if (!can_rx_local(&msg)) continue;
    } else {
      if (!can_rx_remote(s,&msg)) continue;
    }
    print_canmsg(&msg);
  }
}

int can_tx_test(int s) {
  unsigned ui;
  int j;
  canmsg msg;

  if (s >= 0) {
    canmsg_init_cmd0(&msg,0xDEADBEEF,0xFFFFFFFF);
    msg.flags &= ~FLAG_CMD_RXEN;
    can_tx_remote(s,&msg);
  }
  canmsg_init(&msg);
  msg.length = 8;
  for (ui=0;;ui++) {
    gettimeofday(&msg.timestamp,NULL);
    msg.id = ui;
    for (j=0;j<8;j++) msg.data[j] = (ui << 3) + j + 1;
    if (s < 0) {
      can_tx_local(&msg);
    } else {
      can_tx_remote(s,&msg);
    }
  }
}

static int alarmed = 0;
static void alarmsig(int x) {
  alarmed = 1;
}

static void dosig(int x) {
  if (x == SIGHUP) return;
  busunlock();
  exit(0);
}

int can_rx_test(int s) {
  struct sigaction sa;
  struct timeval tv1, tv2;
  unsigned char tmp[16];
  int idseq = -1, err = 1, i, j, k;
  int corrupts = 0;
  struct sched_param sched;
  struct rusage ru1, ru2;
  canmsg msg;

  if (s >= 0) {
    canmsg_init_cmd0(&msg,0,0); // Rx all packets
    msg.flags |= FLAG_CMD_RXEN;
    can_tx_remote(s,&msg);
  }
  nice(-20);
  sched.sched_priority = 50;
  sched_setscheduler(0, SCHED_FIFO, &sched);
  
  sa.sa_handler = alarmsig;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction(SIGALRM, &sa, NULL);
  if (getenv("TS_PRODUCTION") == NULL) alarm(10);
  else alarm(2);
  gettimeofday(&tv1, NULL);
  getrusage(RUSAGE_SELF, &ru1);
  i = 0;
  while (!alarmed) {
    if (s < 0) {
      can_irqwait();
      can_rx_local(&msg);
    } else {
      if (!can_rx_remote(s,&msg)) break;
    }
    if (msg.flags & FLAG_BUS_ERROR) printf("errormsg=\"*** bus error\"\n");
    if (msg.flags & FLAG_ERROR_PASSIVE) printf("errormsg=\"*** error passive\"\n");
    if (msg.flags & FLAG_DATA_OVERRUN) printf("errormsg=\"*** data overrun error\"\n");
    if (msg.flags & FLAG_ERROR_WARNING) printf("errormsg=\"*** error warning\"\n");
    if (msg.length) {
      i++;
      if (idseq == -1) {
	err = 0;
	idseq = msg.id;
      } else {
	int nid = (idseq + 1) & 0x1fffffff;
	if (msg.id != nid) {
	  fprintf(stderr, 
		  "errormsg=\""
		  "ID 0x%x != 0x%x\"\n",
		  msg.id, nid);
	  err++;
	}
	for (k = 0; k < msg.length; k++) 
	  if (msg.data[k] != (((msg.id<<3)+k+1)&0xff)) {
	    fprintf(stderr,
		    "errormsg=\""
		    "data[%d] 0x%x != 0x%x\"\n", 
		    k, msg.data[k], 
		    (((msg.id<<3)+k+1)&0xff));
	    corrupts++;
	  }
	idseq = msg.id;
      }
    }
  }
  /* count packets in i */
  getrusage(RUSAGE_SELF, &ru2);
  gettimeofday(&tv2, NULL);
  j = (tv2.tv_sec - tv1.tv_sec) * 1000;
  j += tv2.tv_usec / 1000 - tv1.tv_usec / 1000;
  printf("total_packets=%d\n",i);
  printf("packets_per_sec=%d\n", (i * 1000) / j);
  i = (ru2.ru_utime.tv_sec + ru2.ru_stime.tv_sec) * 1000;
  i += ru2.ru_utime.tv_usec / 1000 + ru2.ru_stime.tv_usec / 1000;
  i -= (ru1.ru_utime.tv_sec + ru1.ru_utime.tv_sec) * 1000;
  i -= ru1.ru_utime.tv_usec / 1000 + ru1.ru_stime.tv_usec / 1000;
  printf("cpu_utilization_pct=%d\n", i * 100 / j);
  printf("test_sequence_errors=%d\n", err);
  printf("test_corruption_errors=%d\n", corrupts);
  return 0;
}

static void busnop(void) {
}

init_other() {
  buslock = busnop;
  busunlock = busnop;
  buspreempt = busnop;
}
void init_prosoft();
void init_cavium();

void arch_init() {
  cpu = ts_cpu();
  if (cpu == CPU_CAVIUM) {
    init_cavium();
  } else if (cpu == CPU_EP9302) {
    // && ([0x600ff080] >> 4) == 0x8B1
    init_prosoft();
  } else {
    init_other();
  }
}

struct opt_txdata {
  int opt_txdat;
  int txdat_len;
  unsigned char txdat[8];
};

static int parse_txdat(char *s, unsigned char *txdat) {
	unsigned int val, len;
	char *e;

	len = 0;
	while (*s) {
		val = strtoul(s, &e, 0);
		if (s == e) return -1;
		else if (ispunct(*e)) s = e + 1; 
		else s = e;
		*txdat++ = val;
		len++;
		if (len == 8) break;
	}

	return len;	
}

int opt_octets(char *arg,struct opt_txdata *target,int opt) {
  target->opt_txdat = 1;
  target->txdat_len = parse_txdat(optarg, target->txdat);
  if (target->txdat_len == -1) {
    fprintf(stderr, "txdat parse error\n");
    return 0;
  }
  return 1;
}

int opt_int(char *arg,int *target,int opt) {
  if (!arg) {
    *target = 7552;
  } else {
    *target = atoi(arg);
  }
  return 1;
}

/*
void usage(char **argv) {
	fprintf(stderr, "Usage: %s [OPTION] ...\n"
	  "Technologic Systems CAN controller manipulation.\n"
	  "\n"
	  "General options:\n"
	  "  -a, --address=ADR       CAN register address\n"
	  "  -b, --baud=BAUD         CAN baud rate (7500 to 1000000)\n"
	  "  -r, --peek8             CAN register read\n"
	  "  -w, --poke8=VAL         CAN register write\n"
	  "  -t, --txid=ID           CAN TX packet ID\n"
	  "  -d, --txdat=DAT         TX packet with data DAT\n"
          "  -s, --server            Daemonize and run as server\n"
	  "  -p, --port=host<:port>  Talk to canctl server\n"
	  "  -R, --txrtr             TX RTR packet\n"
	  "  -D, --dump              Receive and print all CAN packets\n"
	  "  -0, --btr0=BTR0         SJA1000 BTR0 bus timing reg val\n"
	  "  -1, --btr1=BTR1         SJA1000 BTR1 bus timing reg val\n"
	  "  -x, --txtest            Send TX test pattern\n"
	  "  -y, --rxtest            Do RX test\n"
	  "  -h, --help              This help\n"
	  "\n",
	  argv[0]
	);
}
*/
/*
 valid combinations of command line arguments:
  --address --poke8
            --peek8
  [ ( --baud | --btr0 --btr1 ) | --port ] --server
  [ ( --baud | --btr0 --btr1 ) | --port ] [ --txid ] --txrtr
  [ ( --baud | --btr0 --btr1 ) | --port ] [ --txid ] --txdat
  [ ( --baud | --btr0 --btr1 ) | --port ] --dump
  [ ( --baud | --btr0 --btr1 ) | --port ] --txtest
  [ ( --baud | --btr0 --btr1 ) | --port ] --rxtest
  
 */

int main(int argc, char **argv) {
        int c, cs=-1;
	int opt_address = 0, opt_pokeval = -1, opt_peek8 = 0;
	int opt_txrtr = 0, opt_dump = 0, opt_rxtest = 0;
	int opt_btr0 = 0, opt_btr1 = 0x7e, opt_txtest = 0, opt_baud = 0;
	int opt_server = 0, opt_client = -1;
	char *port;
	unsigned int opt_txid = 0;
	struct opt_txdata opt_txd = { .opt_txdat = 0 };
	struct option2 opts[] = {
	  { 1, (opt_func)opt_long  ,&opt_address,"<a>ddress" , "ADR        CAN register address" },
	  { 1, (opt_func)opt_long  ,&opt_baud   ,"<b>aud" , "BAUD          CAN baud rate (7500 to 1000000)" },
	  { 0, (opt_func)opt_bool  ,&opt_peek8  ,"peek8<R>" , "            CAN register read" },
	  { 1, (opt_func)opt_long  ,&opt_pokeval,"poke8<W>" , "VAL         CAN register write" },
	  { 1, (opt_func)opt_long  ,&opt_txid   ,"tx<i>d" , "ID            CAN TX packet ID" },
	  { 0, (opt_func)opt_bool  ,&opt_txrtr  ,"txr<T>r" , "             TX RTR packet" },
	  { 1, (opt_func)opt_octets,&opt_txd    ,"tx<d>at" , "DAT          TX packet with data DAT" },
	  { 2, (opt_func)opt_int   ,&opt_server ,"<s>erver" , "=<port>     Daemonize and run as server" },
	  { 0, (opt_func)opt_bool  ,&opt_dump   ,"<D>ump" , "              Receive and print all CAN packets" },
	  { 1, (opt_func)opt_long  ,&opt_btr0   ,"btr<0>" , "BTR0          SJA1000 BTR0 bus timing reg val" },
	  { 1, (opt_func)opt_long  ,&opt_btr1   ,"btr<1>" , "BTR1          SJA1000 BTR1 bus timing reg val" },
	  { 0, (opt_func)opt_bool  ,&opt_txtest ,"<t>xtest" , "            Send TX test pattern" },
	  { 0, (opt_func)opt_bool  ,&opt_rxtest ,"<r>xtest" , "            Do RX test" },
	  { 1, (opt_func)opt_cli   ,&opt_client ,"<p>ort" , "<host><:port> Talk to canctl server" },
	  { 0,0,0,"Technologic Systems CAN controller manipulation.\n","" }
	};

	opt_cli_defaultport = PORT;
	process_options(argc,argv,opts);
	// opt.c, add preamble to help text
	arch_init();

	if (irq_num == 0 && opt_client==-1) {
	  printf("Local CAN hardware not present/supported on this architecture.\n");
	  return 1;
	}
	if (opt_client != -1) {
	  struct sigaction sa;

	  sa.sa_handler = dosig;
	  sigemptyset(&sa.sa_mask);
	  sa.sa_flags = 0;
	  sigaction(SIGINT, &sa, NULL);
	  sigaction(SIGHUP, &sa, NULL);
	  sigaction(SIGTERM, &sa, NULL);

	  atexit(busunlock);
	}


	if (opt_client == -1) { // stuff for direct CAN access only
	  if (opt_baud) {
	    init_baud(opt_baud,&opt_btr0,&opt_btr1);
	  }	  
	  if (opt_pokeval != -1) {
	    buslock();
	    can_poke8(opt_address, opt_pokeval);
	    busunlock();
	    return 0;
	  }
	  if (opt_peek8) {
	    unsigned int x;
	    buslock();
	    x = can_peek8(opt_address);
	    busunlock();
	    printf("0x%x\n", x);
	    return 0;
	  }
	  if (opt_server) {
	    return do_server(opt_btr0,opt_btr1);
	  } else {
	    if (open_irq() < 0) {
	      printf("Error accessing CAN IRQ\n");
	      return 1;
	    }
	    init_can(opt_btr0,opt_btr1);
	  }
	} else { // initialize client socket
	  cs = opt_client;
	}

        if (opt_txtest) {
	  can_tx_test(cs);
	} else if (opt_txd.opt_txdat || opt_txrtr) {
	  canmsg msg;

	  canmsg_init_tx(&msg,opt_txid,opt_txrtr ? FLAG_RTR : 0,opt_txd.txdat_len,opt_txd.txdat);
	  if (opt_client != -1) {
	    return can_tx_remote(cs,&msg);
	  } else {
	    return can_tx_local(&msg);
	  }
	} else if (opt_dump) {
	  can_rx_dump(cs);
	} else if (opt_rxtest) {
	  return can_rx_test(cs);
	}

	return 0;
}

//==================================================================
// NETWORK SERVER CODE BEGINS
SC *new_server_conn(int s) {
  struct instance_data *idata = malloc(sizeof(struct instance_data));

  idata->desc_type = 0;
  idata->id = 0;
  idata->mask = 0;
  idata->flags = 0;
  SC *conn = malloc(sizeof(SC));
  conn->s = s;
  conn->buf = malloc(sizeof(canmsg));
  conn->len = sizeof(canmsg);
  conn->used = 0;
  conn->want = sizeof(canmsg);
  conn->inst_data = idata;
  conn->next = 0;
  conn->wbuf = malloc(sizeof(canmsg));
  conn->wlen = 0;
  conn->wwr = 0;
  int whead,wtail;

  return conn;
}

int rx_packet_q() {
  int n;

  buslock();
  n = can_peek8(29);
  if (n > 128) n &= 0x7F;
  busunlock();
  return n;
}

int calc_usec(struct timeval *tv1,struct timeval *tv2) {
  int us;
  us = tv2->tv_usec - tv1->tv_usec + (tv2->tv_sec - tv1->tv_sec) * 1000000;
  *tv1 = *tv2;
  return us;
}

void server_rx(SC *head) {
  int dummy;
  canmsg msg;
  SC *cur;

  while (1) {
    cur = head;
    canmsg_init(&msg);
    if (!can_rx_local(&msg)) {
      return;
    }
    if (msg.flags || msg.length) {
      while (cur) {
	struct instance_data *idata = 
	  (struct instance_data *)cur->inst_data;
	if (idata->desc_type == 0
	    && idata->flags & FLAG_CMD_RXEN
	    && !((msg.id ^ idata->id) & idata->mask)) {
	  if ((cur->wlen - cur->wwr) > 0) {
	    printf("net congestion - dropped packet\n");
	    // should only reach here if client isn't reading fast enough!
	  } else {
	    memcpy(cur->wbuf,&msg,sizeof(canmsg));
	    cur->wlen = sizeof(canmsg);
	    cur->wwr = 0;
	    bwrite(cur->s,cur->wbuf,cur->wlen,&cur->wwr);
	    if (head->wfd && cur->wwr < cur->wlen) {
	      FD_SET(cur->s,head->wfd);
	    }
					}
	}
	cur = cur->next;
      }
    }
  }  
}

void server_cmd(SC *conn,canmsg *cmd) {
  struct instance_data *idata = conn->inst_data;
  int i;

  switch (cmd->length) {
  case 0: // acceptance filter
    i = ntoh_UINT32_LE(cmd->data);
    idata->id = i;
    i = ntoh_UINT32_LE(cmd->data+4);
    idata->mask = i;
    idata->flags = cmd->flags;
    break;
  }
}

int server_service(SC *conn,void *data,SC *head) {
  struct instance_data *idata = conn->inst_data;
  struct server_data *sdata = data;
  unsigned char tmp[16];
  unsigned int id;
  int flags;
  int j;
  static int lastid = -1;

  if (idata->desc_type == 0) { // socket
    canmsg *msg = (canmsg *)conn->buf;

    //printf("incoming data from socket\n");
    //print_canmsg(msg);
    //fprintf(stderr,"(%d)\n",msg->id);
    if (msg->flags & FLAG_CONTROL) {
      server_cmd(conn,msg);
    } else {
      while (!can_tx_local_nonblocking(msg)) {
	can_irqwait_noack(0);
	if (j = can_tx_local_errorq()) {
	  printf("txerror=%d\n",j); // tx error
	}
	server_rx(head);
	if (can_irqwait_noack(1)) {
	  read(head->s,&j,4); // acknowledge IRQ
	}
      }
    }
  } else { // CAN IRQ
    // already going to rx in both cases, in case we got a packet during tx
    // so no need to do it here especially
  }
  server_rx(head);
  if (can_irqwait_noack(1)) {
    read(head->s,&j,4); // acknowledge IRQ
  }
  conn->used = 0;
  return 1;
}

// NETWORK SERVER CODE ENDS
//==================================================================


/*
  Supplemental Code
  Translation of raw packet format to/from Lincan's canmsg_t struct

  Note that it is not possible to translate all canctl features to
  the lincan API structure.

// Requires including the appropriate LinCAN header files
// Uncomment everything after this line to use
void canctl2lincan(canmsg *cc,struct canmsg_t *lc) {
  int i;

  lc->flags = 0;
  if (cc->flags & FLAG_RTR) {
    lc->flags |= MSG_RTR;
  }
  if (cc->flags & FLAG_EXT_ID) {
    lc->flags |= MSG_EXT;
  }
  if (cc->flags & FLAG_LOCAL) {
    lc->flags |= MSG_LOCAL;
  }
  lc->cob = 0;
  lc->id = cc->id;
  lc->timestamp = cc->timestamp;
  lc->length = cc->length;
  for (i=0;i<CAN_MSG_LENGTH;i++) {
    lc->data[i] = cc->data[i];
  }
}

void lincan2canctl(struct canmsg_t *lc,canmsg *cc) {
  int i;
 
  canmsg_init(cc);
  lc->flags = 0;
  if (lc->flags & MSG_RTR) {
    cc->flags |= FLAG_RTR;
  }
  if (lc->flags & MSG_EXT) {
    cc->flags |= FLAG_EXT_ID;
  }
  if (lc->flags & MSG_LOCAL) {
    cc->flags |= FLAG_LOCAL;
  }
  cc->id = lc->id;
  cc->timestamp = lc->timestamp;
  cc->length = lc->length;
  for (i=0;i<CAN_MSG_LENGTH;i++) {
    cc->data[i] = lc->data[i];
  }
}
*/

/*
  net rx -> can tx
106.98 TOT, 35.71 USER, 55.05 SYS (33%,52%)
  can rx ->
647.05 TOT, 161.5 USER, 181.8 SYS (25%,28%)

  can rx -> net tx
411.60 TOT, 110.8 USER, 201.8 SYS (27%,49%)

 */
