#include <stdio.h>
#include <errno.h>

#include "sock.h"
#include "server.h"
#define type server_connection
#include "list.h"


#include "co.h"
#include "dioctl.h"
#include "dioname.h"

int BusCyclesLog=0;
#define NOSIG(expression)			\
  (__extension__				\
   ({ long int __result;			\
     do __result = (long int) (expression);	\
     while (__result == -1L && errno == EINTR);	\
     __result; }))

// arch dependant
extern uint8 DioCapabilities[256];
extern uint8 TotalDioCount;
void (*SetDioDir)(int dionum,int asOutput);
void (*SetDio)(int dionum,int asHigh);
int (*GetDioDir)(int dionum);
int (*GetDio)(int dionum);
void (*CommitDio)();

int modelId=-1;

typedef struct server_data server_data;
struct server_data {
  server_connection *head;
};

typedef struct instance_data instance_data;

struct instance_data {
  void *context;
  int maplen[16];
  int *diomap[16];
  struct timeval lastEvent;
  int arm;
};

instance_data *new_instance_data() {
  instance_data *inst_data = malloc(sizeof(instance_data));
  int i;

  inst_data->context = 0;
  for (i=0;i<16;i++) {
    inst_data->maplen[i] = 0;
    inst_data->diomap[i] = 0;
    inst_data->lastEvent.tv_sec = 0;
    inst_data->lastEvent.tv_usec = 0;
    inst_data->arm = 0;
  }
  return inst_data;
}

void delete_instance_data(instance_data *inst) {
  typedef struct context {
    void *line;
    uint8 *dir,*val;
    unsigned char cmd,byte,dionum,mapnum,numdios;
    unsigned char *name;
    unsigned short value,mask,min,max;
    int i;
    struct timeval tv;
  } context;
  context *ctx;

  if (inst) {
    if (inst->context) {
      ctx = inst->context;
      if (ctx->dir) free(ctx->dir);
      if (ctx->val) free(ctx->val);
      if (ctx->name) free(ctx->name);
      free(inst->context);
    }
    free(inst);
  }
}

#define WAIT(x) while (!(x)) { coReturn(0); }
#define LOCK(id,res) while (!lock_request(id,res)) { coReturn(0); }
#define MULTILOCK(id,len,map) while (!lock_request_multiple(id,len,map)) { fprintf(stderr,"MULTILOCK %d\n",conn->s); coReturn(0); }
#define UNLOCK(id,res) lock_release(id,res);

uint8 DioValue[256];

extern int MapComparePartial(Map a,Map b);

int server_processing(coParm,server_connection *conn) {
  coBeginContext;
  uint8 *dir,*val;
  unsigned char cmd,byte,dionum,mapnum,numdios;
  unsigned char *name;
  unsigned short value,mask,min,max;
  int i;
  struct timeval tv;
  coEndContext;

  instance_data *inst_data = conn->inst_data;

  coBegin {
    WAIT(ring_read_byte(conn->rx,&co(cmd)));
    //fprintf(stderr,"CMD=%d, conn=%d, ctx=%p, dir=%p\n",co(cmd),conn->s,__ctx,co(dir));
    switch(co(cmd)) {
    case 0: // netDIOCapabilitiesGet
      WAIT(ring_write_byte(conn->tx,TotalDioCount));
      for (co(i)=0;co(i)<TotalDioCount;co(i)++) {
	WAIT(ring_write_byte(conn->tx,DioCapabilities[co(i)]));
      }
      break;
    case 1: // netDIOSetInput
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(dionum) = co(byte);
      LOCK(conn->s,co(dionum));
      SetDioDir(co(dionum),0);
      UNLOCK(conn->s,co(dionum));
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 2: // netDIOSetHigh
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(dionum) = co(byte);
//fprintf(stderr,"high lock for %d\n",conn->s);
      LOCK(conn->s,co(dionum));
//fprintf(stderr,"high locked\n");
      SetDioDir(co(dionum),1);
      SetDio(co(dionum),1);
      UNLOCK(conn->s,co(dionum));
//fprintf(stderr,"high unlocked\n");
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 3: // netDIOSetLow
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(dionum) = co(byte);
//fprintf(stderr,"low lock for %d\n",conn->s);
      LOCK(conn->s,co(dionum));
//fprintf(stderr,"low locked\n");
      SetDioDir(co(dionum),1);
      SetDio(co(dionum),0);
      UNLOCK(conn->s,co(dionum));
//fprintf(stderr,"low unlocked\n");
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 4: // netDIOGet
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(dionum) = co(byte);
      co(byte) = (GetDioDir(co(dionum)) << 7) + (GetDio(co(dionum)) << 6);
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      WAIT(ring_write_byte(conn->tx,co(byte)));
      break;
    case 5: // netDIOGroupDefine
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
      co(numdios) = 1 + (co(byte) & 0xF);
      if (inst_data->diomap[co(mapnum)]) {
	free(inst_data->diomap[co(mapnum)]);
      }
      //fprintf(stderr,"group %d ",co(mapnum));
      inst_data->maplen[co(mapnum)] = co(numdios);
      inst_data->diomap[co(mapnum)] = malloc(co(numdios) * sizeof(int));
      for (co(i)=0;co(i)<co(numdios);co(i)++) {
	WAIT(ring_read_byte(conn->rx,&co(byte)));
	inst_data->diomap[co(mapnum)][co(i)] = co(byte);
	//fprintf(stderr,"%d ",co(byte));
      }
      //fprintf(stderr,"\n");
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 6: // netDIOGroupSetInput
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
      MULTILOCK(conn->s,inst_data->maplen[co(mapnum)],inst_data->diomap[co(mapnum)]);
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	SetDioDir(inst_data->diomap[co(mapnum)][co(i)],0);
      }
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	UNLOCK(conn->s,inst_data->diomap[co(mapnum)][co(i)]);
      }
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 7: // netDIOGroupSetHigh
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
      MULTILOCK(conn->s,inst_data->maplen[co(mapnum)],inst_data->diomap[co(mapnum)]);
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	SetDioDir(inst_data->diomap[co(mapnum)][co(i)],1);
	SetDio(inst_data->diomap[co(mapnum)][co(i)],1);
      }
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	UNLOCK(conn->s,inst_data->diomap[co(mapnum)][co(i)]);
      }
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 8: // netDIOGroupSetLow
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
      MULTILOCK(conn->s,inst_data->maplen[co(mapnum)],inst_data->diomap[co(mapnum)]);
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	SetDioDir(inst_data->diomap[co(mapnum)][co(i)],1);
	SetDio(inst_data->diomap[co(mapnum)][co(i)],0);
      }
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	UNLOCK(conn->s,inst_data->diomap[co(mapnum)][co(i)]);
      }
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 9: // netDIOGroupSet
      //fprintf(stderr,"netDIOGroupSet\n");
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
      // get all the states first before setting anything
      // in case we don't have enough data to do it all
      co(dir) = malloc(inst_data->maplen[co(mapnum)]);
      co(val) = malloc(inst_data->maplen[co(mapnum)]);
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	if (co(i) % 4 == 0) WAIT(ring_read_byte(conn->rx,&co(byte)));
	co(dir)[co(i)] = (co(byte) >> 7) & 1;
	co(val)[co(i)] = (co(byte) >> 6) & 1;
	co(byte) <<= 2;
      }
      //fprintf(stderr,"netDIOGroupSet locking...\n");
      MULTILOCK(conn->s,inst_data->maplen[co(mapnum)],inst_data->diomap[co(mapnum)]);
      //fprintf(stderr,"netDIOGroupSet locked\n");

      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	SetDioDir(inst_data->diomap[co(mapnum)][co(i)],co(dir[co(i)]));
	SetDio(inst_data->diomap[co(mapnum)][co(i)],co(val[co(i)]));
      }
      free(co(dir));
      co(dir) = 0;
      free(co(val));
      co(val) = 0;
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	UNLOCK(conn->s,inst_data->diomap[co(mapnum)][co(i)]);
      }
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 10: // netDIOGroupGet
      //fprintf(stderr,"netDIOGroupGet\n");
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
      co(byte) = 0;
      //fprintf(stderr,"mapnum=%d\n",co(mapnum));
      // first, write the number of states we are going to send back
      WAIT(ring_write_byte(conn->tx,inst_data->maplen[co(mapnum)]));
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	co(byte) |= ((!!GetDioDir(inst_data->diomap[co(mapnum)][co(i)])) << (7-2*(co(i)%4)));
	co(byte) |= !!GetDio(inst_data->diomap[co(mapnum)][co(i)]) << (6-2*(co(i)%4));
	// might it be better to read everything to a buffer first?
	// that way, if ring_write_byte blocks, we at least have the state
	// of the DIOs all at the same time.
	if ((co(i) % 4 == 3) || (co(i)+1==inst_data->maplen[co(mapnum)])) {
	  WAIT(ring_write_byte(conn->tx,co(byte)));
	  co(byte) = 0;
	}
      }
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 11: // netDIOGroupLockBlocking
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
      //fprintf(stderr,"netDIOGroupLockBlocking %d\n",co(mapnum));
      MULTILOCK(conn->s,inst_data->maplen[co(mapnum)],inst_data->diomap[co(mapnum)]);
      //fprintf(stderr,"netDIOGroupLockBlocking locked\n",co(mapnum));
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 12: // netDIOGroupLockRelease
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	//printf("unlock %d by %d\n",inst_data->diomap[co(mapnum)][co(i)],conn->s);
	      UNLOCK(conn->s,inst_data->diomap[co(mapnum)][co(i)]);
      }
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 13: // netDIOGroupLockNonBlocking
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
//printf("nonblocking lock on group %d\n",co(mapnum));
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
//printf("#%d:",co(i)); fflush(stdout);
//printf("pin %d:",inst_data->diomap[co(mapnum)][co(i)]); fflush(stdout);
	if (lockedQ(conn->s,inst_data->diomap[co(mapnum)][co(i)])) {
//	  printf("locked=1\n"); fflush(stdout);
	  break;
	} else {
//	  printf("locked=0\n"); fflush(stdout);
	}
      }
//printf("%d<%d?\n",co(i),inst_data->maplen[co(mapnum)]);
      if (co(i) < inst_data->maplen[co(mapnum)]) {
        if (inst_data->arm) {
          inst_data->arm = 0;
	  gettimeofday(&inst_data->lastEvent,0);
        }
//printf("nonblocking lock fails\n");
	WAIT(ring_write_byte(conn->tx,0x5A));
      } else {
//printf("nonblocking lock looks to succeed\n");
	MULTILOCK(conn->s,inst_data->maplen[co(mapnum)],inst_data->diomap[co(mapnum)]);
        if (inst_data->arm) {
          inst_data->arm = 0;
	  gettimeofday(&inst_data->lastEvent,0);
        }
	WAIT(ring_write_byte(conn->tx,0x77));
//printf("nonblocking lock succeeds\n");
      }
      break;
      case 14: // netDIOGroupWaitOld
fprintf(stderr,"netDIOGroupWaitOld\n");
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
      // get all the states first before setting anything
      // in case we don't have enough data to do it all yet
      co(val) = malloc(inst_data->maplen[co(mapnum)]);

      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	if (co(i) % 8 == 0) WAIT(ring_read_byte(conn->rx,&co(byte)));
	co(val)[co(i)] = (co(byte) >> 7) & 1;
	co(byte) <<= 1;
      }
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	request_watch(conn->s,inst_data->diomap[co(mapnum)][co(i)],co(val)[co(i)]);
      }
      // now wait
      do {
	co(byte) = 0;
	for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	  if (DioValue[inst_data->diomap[co(mapnum)][co(i)]] == co(val)[co(i)]) {
	    co(byte)++;
	  }
	}
	if (co(byte) == 0) coReturn(0);
      } while (co(byte) == 0);
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      // now get all the values in the group
      co(dir) = malloc(co(byte));
      co(byte) = 0;
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	if (DioValue[inst_data->diomap[co(mapnum)][co(i)]] == co(val)[co(i)]) {
	  co(dir)[co(byte)++] = co(i);
	}
      }
      WAIT(ring_write_byte(conn->tx,co(byte)));
      for (co(i)=0;co(i) < co(byte);co(i)++) {
	WAIT(ring_write_byte(conn->tx,co(i)));
      }
      // release values and watches
      free(co(dir));
      free(co(val));
      co(dir) = 0;
      co(val) = 0;
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	release_watch(conn->s);
      }
      break;
    case 114: // netDIOGroupWait
      //fprintf(stderr,"netDIOGroupWait\n");
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mapnum) = co(byte) >> 4;
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(value) = 256 * (unsigned short)co(byte);
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(value) += co(byte);
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mask) = 256 * (unsigned short)co(byte);
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(mask) += co(byte);
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(min) = 256 * (unsigned short)co(byte);
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(min) += co(byte);
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(max) = 256 * (unsigned short)co(byte);
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(max) += co(byte);
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	if (((co(mask)>>co(i))&1)) {
		request_watch(conn->s,
			inst_data->diomap[co(mapnum)][co(i)],
			((co(value)>>co(i))&1));
	}
      }
      // now wait
      do {
	co(byte) = 0;
	for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	  if (((co(mask)>>co(i))&1) &&DioValue[inst_data->diomap[co(mapnum)][co(i)]] == ((co(value)>>co(i))&1)) {
	    co(byte)++;
	  }
	}
	if (co(byte) < co(min) || co(byte) > co(max)) coReturn(0);
      } while (co(byte) < co(min) || co(byte) > co(max));
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      // now get all the values in the group
      co(value) = 0;
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	co(value) |= (DioValue[inst_data->diomap[co(mapnum)][co(i)]] << co(i));
      }
      WAIT(ring_write_byte(conn->tx,co(value)>>8));
      WAIT(ring_write_byte(conn->tx,co(value)&0xFF));
      // release watches
      for (co(i)=0;co(i)<inst_data->maplen[co(mapnum)];co(i)++) {
	release_watch(conn->s);
      }
      break;
    case 15: // netDIOWaitLow
      WAIT(ring_read_byte(conn->rx,&co(dionum)));
      request_watch(conn->s,co(dionum),0);
      while (DioValue[co(dionum)] == 1) coReturn(0);
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      release_watch(conn->s);
      WAIT(ring_write_byte(conn->tx,co(dionum)));
    break;
    case 16: // netDIOWaitHigh
      WAIT(ring_read_byte(conn->rx,&co(dionum)));
      request_watch(conn->s,co(dionum),1);
      while (DioValue[co(dionum)] == 0) coReturn(0);
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      release_watch(conn->s);
      WAIT(ring_write_byte(conn->tx,co(dionum)));
      break;
    case 17: // netDIOLockBlocking
      WAIT(ring_read_byte(conn->rx,&co(dionum)));
      LOCK(conn->s,co(dionum));
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 18: // netDIOLockRelease
      WAIT(ring_read_byte(conn->rx,&co(dionum)));
//printf("unlock %d by %d\n",co(dionum),conn->s);
      UNLOCK(conn->s,co(dionum));
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 19: // netDIOLockNonBlocking
      WAIT(ring_read_byte(conn->rx,&co(dionum)));
      if (lockedQ(conn->s,co(dionum))) {
        if (inst_data->arm) {
          inst_data->arm = 0;
	  gettimeofday(&inst_data->lastEvent,0);
        }
	WAIT(ring_write_byte(conn->tx,0x5A));
      } else {
	LOCK(conn->s,co(dionum));
        if (inst_data->arm) {
          inst_data->arm = 0;
	  gettimeofday(&inst_data->lastEvent,0);
        }
	WAIT(ring_write_byte(conn->tx,0x77));
      }
      break;
    case 20: // netDIOGroupWaitLow
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 21: // netDIOGroupWaitHigh
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
    case 22: // netBoardModelGet
      WAIT(ring_write_byte(conn->tx,modelId >> 24));
      WAIT(ring_write_byte(conn->tx,modelId >> 16));
      WAIT(ring_write_byte(conn->tx,modelId >> 8));
      WAIT(ring_write_byte(conn->tx,modelId));
      break;
     case 23: // netDIONameLookup
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(name) = malloc(2+co(byte));
      for (co(i)=0;co(i)<co(byte);co(i)++) {
	WAIT(ring_read_byte(conn->rx,co(name)+co(i)));
      }
      co(name)[co(i)] = 0;
      {
	      Map val,*got;
	      val.key = (char *)co(name);
	      got=MapFindBinary(DIOMapNamePin,val,MapCompareKey);
	      if (got) {
		      co(i) = got->val;
	      } else {
//fprintf(stderr,"%d elements\n",ArrayLen(DIOMapNamePin));
		      co(i) = -1;
	      }
      }
      free(co(name));
      co(name) = 0;
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      WAIT(ring_write_byte(conn->tx,co(i) == -1 ? 0:1));
      WAIT(ring_write_byte(conn->tx,co(i)));
      break;
      case 27: // netDIOLookup
	WAIT(ring_read_byte(conn->rx,&co(byte)));
	co(name) = malloc(2+co(byte));
	for (co(i)=0;co(i)<co(byte);co(i)++) {
	  WAIT(ring_read_byte(conn->rx,co(name)+co(i)));
	}
	co(name)[co(i)] = 0;
	WAIT(ring_read_byte(conn->rx,&co(byte)));
	{
	  Map val,*got;
	  val.key = co(name);
	  val.val = co(byte);
	  got=MapFindBinary(DIOMapNamePin,val,MapComparePartial);
	  free(co(name));
	  co(name) = 0;
	  if (!got) {
	    co(name) = "";
	  } else {
	    co(name) = got->key;
	  }
	  co(byte) = strlen(co(name));
	}
	if (inst_data->arm) {
	  inst_data->arm = 0;
	  gettimeofday(&inst_data->lastEvent,0);
	}
	// send back full name
	WAIT(ring_write_byte(conn->tx,co(byte)));
	for (co(i)=0;co(i)<co(byte);co(i)++) {
	  WAIT(ring_write_byte(conn->tx,co(name)[co(i)]));
	}
      break;
     case 24: // netDIONameAssign
      WAIT(ring_read_byte(conn->rx,&co(dionum)));
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(name) = malloc(2+co(byte));
      for (co(i)=0;co(i)<co(byte);co(i)++) {
	WAIT(ring_read_byte(conn->rx,co(name)+co(i)));
      }
      co(name)[co(i)] = 0;
      {
	      Map val;
	      val.key = strdup(co(name));
	      val.val = co(dionum);
	      if (!MapFindBinary(DIOMapNamePin,val,MapCompareKey)) {
		MapInsertSorted(&DIOMapNamePin,val,MapCompareKey);
	      }
      }
      free(co(name));
      co(name) = 0;
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
     case 25: // netDIONameRemove
      WAIT(ring_read_byte(conn->rx,&co(byte)));
      co(name) = malloc(2+co(byte));
      for (co(i)=0;co(i)<co(byte);co(i)++) {
	WAIT(ring_read_byte(conn->rx,co(name)+co(i)));
      }
      co(name)[co(i)] = 0;
      {
	      Map val,*got;
	      val.key = co(name);
	      got = MapFindBinary(DIOMapNamePin,val,MapCompareKey);
	      if (got) {
		MapDelete(&DIOMapNamePin,got-DIOMapNamePin,1);
	      }
      }
      free(co(name));
      co(name) = 0;
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      break;
     case 26: // netDIONamesList
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
      co(i) = ArrayLen(DIOMapNamePin);
      if (co(i) > 65535) { // oops!
	      co(i) = 65535;
      }
      WAIT(ring_write_byte(conn->tx,co(i)>>8));
      WAIT(ring_write_byte(conn->tx,co(i)&0xFF));
      co(value) = co(i);
      for (co(i)=0;co(i)<co(value);co(i)++) {
	WAIT(ring_write_byte(conn->tx,DIOMapNamePin[co(i)].val));
	co(max) = strlen(DIOMapNamePin[co(i)].key);
	WAIT(ring_write_byte(conn->tx,co(max)));
	for (co(byte)=0;co(byte)<co(max);co(byte)++) {
	  WAIT(ring_write_byte(conn->tx,DIOMapNamePin[co(i)].key[co(byte)]));
	}
      }
      break;
      /*
     case 27: // netDIOConnectorList is obsolete
      if (inst_data->arm) {
	inst_data->arm = 0;
	gettimeofday(&inst_data->lastEvent,0);
      }
       for (co(i)=0;co(i)<200;co(i)++) {
	       WAIT(ring_write_byte(conn->tx,0)); // obsolete
       }
     break;
      */       
     case 28: // netDIOTimeEventGet
       WAIT(ring_write_byte(conn->tx,inst_data->lastEvent.tv_sec >> 24));
       WAIT(ring_write_byte(conn->tx,inst_data->lastEvent.tv_sec >> 16));
       WAIT(ring_write_byte(conn->tx,inst_data->lastEvent.tv_sec >> 8));
       WAIT(ring_write_byte(conn->tx,inst_data->lastEvent.tv_sec));
       WAIT(ring_write_byte(conn->tx,inst_data->lastEvent.tv_usec >> 24));
       WAIT(ring_write_byte(conn->tx,inst_data->lastEvent.tv_usec >> 16));
       WAIT(ring_write_byte(conn->tx,inst_data->lastEvent.tv_usec >> 8));
       WAIT(ring_write_byte(conn->tx,inst_data->lastEvent.tv_usec));
     break;
     case 29: // netDIOTimeCurrentGet
       gettimeofday(&co(tv),0);
       WAIT(ring_write_byte(conn->tx,co(tv).tv_sec >> 24));
       WAIT(ring_write_byte(conn->tx,co(tv).tv_sec >> 16));
       WAIT(ring_write_byte(conn->tx,co(tv).tv_sec >> 8));
       WAIT(ring_write_byte(conn->tx,co(tv).tv_sec));
       WAIT(ring_write_byte(conn->tx,co(tv).tv_usec >> 24));
       WAIT(ring_write_byte(conn->tx,co(tv).tv_usec >> 16));
       WAIT(ring_write_byte(conn->tx,co(tv).tv_usec >> 8));
       WAIT(ring_write_byte(conn->tx,co(tv).tv_usec));
       break;
    case 30: // netDIOTimeArm
       inst_data->arm = 1;
       break;
    default:
      // to do: release all locks and close the connection
      break;
    }
//fprintf(stderr,"CMD=%d conn=%d done\n",co(cmd),conn->s);
  } coEnd;
  return 1;
}

int server_frontend(server_connection *conn,void *data) {
  server_data *svr_data = data;
  int i,val;
  
  if (!conn) { // timeout
    for (i=0;i<TotalDioCount;i++) {
      val = GetDio(i);
      if (val != DioValue[i]) {
	DioValue[i] = val;
	watch_update_resource(i,val);
      }
    }
    CommitDio(0);
  } else if (!data) { // exception
  } else { // normal service
    if (conn == svr_data->head) { // new connection request
      struct sockaddr_in csa;
      unsigned size_csa = sizeof(csa);

      i = NOSIG(accept(conn->s, (struct sockaddr *)&csa, &size_csa));
      fileSetBlocking(i,0);
      if (i >= 0) {
	instance_data *inst_data = new_instance_data();
	server_connection *newconn;
	newconn = new_server_connection(i,inst_data,256,256);
	server_connectionQueue(&svr_data->head,newconn);
      }
    } else { // client request
      instance_data *inst_data = conn->inst_data;
      while(server_processing(&inst_data->context,conn));
      CommitDio(1);
    }
  }
  return 1;
}


void run_dioctl_server(int port,int pollrate) {
  server_data svr_data;
  int s;
  instance_data *inst_data = new_instance_data();

  s = create_server_socket(port);
  if (s == -1) {
    fprintf(stderr,"Cannot start server\n");
    return;
  }
  svr_data.head = new_server_connection(s,inst_data,1,0);

  server(server_frontend,&svr_data,&svr_data.head,pollrate,delete_instance_data);
}

/*
  Author: Michael Schmidt (michael@embeddedARM.com)
 */
