/* Copyright (c) 2005-2007 DN-Systems GmbH http://rfdump.org

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */

/* $Header: /cvs/rfdump/src/acg.c,v 1.17 2007/12/08 19:24:06 dirk Exp $ */

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "rfid.h"

static int acg_readconfig(int fd, unsigned char *p, int begin, int end)
{
  int i;
  for(i=begin; i!=end; ++i)
    {
      char buf[16];
      snprintf(buf, sizeof(buf), "rp%02x", i);
      if(rfidWrite(fd, buf) < 0)
	return -1;

      rfidReadLine(fd, buf, sizeof(buf));
      buf[2]=0;
      *p++=strtoul(buf, NULL, 16);
    }
  return 0;
}

static int acg_writeconfig(int fd, unsigned char *p, int begin, int end, int *skiplist)
{
  int i;
  for(i=begin; i!=end; ++i, ++p)
    {
      if(skiplist)
	{
	  int *l=skiplist;
	  while(*l!=-1)
	    {
	      if(*l == i)
		goto skip;
	      ++l;
	    }
	}

      char buf[16];
      snprintf(buf, sizeof(buf), "wp%02X%02X", i, *p);
      if(rfidWrite(fd, buf) < 0)
	return -1;

      if(rfidReadLine(fd, buf, sizeof(buf)) < 0)
	return -1;

      const int len=strlen(buf);
      if(len<2 || (!isxdigit(buf[0]) && !isxdigit(buf[1])))
	{
	  fprintf(stderr, "error writing eeprom byte %02X=%02X: %s\n", i, *p, buf);
	  return -1;
	}
      int j=strtoul(buf, NULL, 16);
      if(j!=*p)
	{
	  fprintf(stderr, "error setting eeprom byte %02X to %02x != %02X '%s'\n", i, *p, j, buf);
	  return -1;
	}

    skip: ;
    }
  return 0;
}

/***************************************************************************/

#pragma pack(push, 1)
struct eeprom_dual_2_2
{
  char device_id[5];		/* 00-04 */
  char admin_data[5];		/* 05-09 */
  unsigned char station_id;	/* 0A */

  union {
    unsigned char pcon;		/* 0B */
    struct {
      unsigned auto_start : 1;
      unsigned protocol : 1;
      unsigned multitag : 1;
      unsigned new_serial_mode : 1;
      unsigned led : 1;
      unsigned single_shot : 1;
      unsigned extended_protocol : 1;
      unsigned extended_id : 1;
    };
  };

  unsigned char baud_rate;	/* 0C */
  unsigned char command_guard_time; /* 0D */

  union {
    unsigned char operation_mode; /* 0E */
    struct {
      unsigned opmode_iso14443a : 1;
      unsigned opmode_iso14443b : 1;
      unsigned opmode_sr176 : 1;
    };
  };

  unsigned char single_shot_timeout; /* 0F */
  unsigned char nix1[3];	/* 10-12 */

  union {
    unsigned char pcon2;	/* 13 */
    struct {
      unsigned disable_multitag_reset : 1;
      unsigned disable_startup_msg : 1;
      unsigned enable_binary_frame_v2 : 1;
      unsigned noisy_env : 1;
      unsigned reset_recovery_time_multiplier : 2;
      unsigned enable_iso14443b_anticollision : 1;
      unsigned disable_iso14434_error_handling : 1;
    };
  };

  unsigned char reset_off_time;	/* 14 */
  unsigned char reset_recovery_time; /* 15 */
  unsigned char application_familiy_id;	/* 16 */
  unsigned char iso14443a_selection_timeout; /* 17 */
  unsigned char iso14443b_selection_timeout; /* 18 */
  unsigned char sr176_selection_timeout; /* 19 */
  unsigned char rfu1;		/* 1A */

  union {
    unsigned char pcon3;	/* 1B */
    struct {
      unsigned disable_automatic_iso144434_timeouts : 1;
      unsigned rfu_1 : 1;
      unsigned page_read : 1;
      unsigned int_1 : 3;
      unsigned reqa_extendend_id : 1;
    };
  };

  unsigned char page_start;	/* 1C */
  unsigned char nix2[2];	/* 1D-1E */
  unsigned char page_num;	/* 1F */
};
#pragma pack(pop)

int acg_printinfo_dual_2_2(int fd)
{
  struct eeprom_dual_2_2 e;
  if(acg_readconfig(fd, (unsigned char*)&e, 0, sizeof(e)) < 0)
    {
      fprintf(stderr, "could not read eeprom config\n");
      return -1;
    }

  fprintf(stderr, "ACG DUAL ISO 2.2 EEPROM contents:\n");
  fprintf(stderr, "  device ID: %02X%02X%02X%02X%02X\n", e.device_id[0], e.device_id[1], e.device_id[2], e.device_id[3], e.device_id[4]);
  fprintf(stderr, "  admin data: %02X%02X%02X%02X%02X\n", e.admin_data[0], e.admin_data[1], e.admin_data[2], e.admin_data[3], e.admin_data[4]);
  fprintf(stderr, "  Station ID: %02X\n", e.station_id);

  fprintf(stderr, "  PCON1: %02X\n", e.pcon);
  fprintf(stderr, "    auto start: %i\n", e.auto_start);
  fprintf(stderr, "    protocol: %i = %s\n", e.protocol, e.protocol?"binary":"ASCII");
  fprintf(stderr, "    multitag: %i\n", e.multitag);
  fprintf(stderr, "    new serial mode: %i\n", e.new_serial_mode);
  fprintf(stderr, "    led: %i\n", e.led);
  fprintf(stderr, "    single shot: %i\n", e.single_shot);
  fprintf(stderr, "    extended protocol: %i\n", e.extended_protocol);
  fprintf(stderr, "    extended id: %i\n", e.extended_id);

  fprintf(stderr, "  Baud rate: %02X = ", e.baud_rate);
  switch(e.baud_rate & 7)
    {
    case 0: fprintf(stderr, "9600\n"); break;
    case 1: fprintf(stderr, "19200\n"); break;
    case 2: fprintf(stderr, "38400\n"); break;
    case 3: fprintf(stderr, "57600\n"); break;
    case 4: fprintf(stderr, "115200\n"); break;
    case 5: fprintf(stderr, "230400\n"); break;
    case 6: fprintf(stderr, "460800\n"); break;
    default: fprintf(stderr, "unknown: %i\n", e.baud_rate & 7); break;
    }

  fprintf(stderr, "  Command Guard Time: %02X = %.2fus\n", e.command_guard_time, 37.8f*e.command_guard_time);

  fprintf(stderr, "  Operation Mode: %02X = ", e.operation_mode);
  if(e.opmode_iso14443a)
    fprintf(stderr, "ISO14443A ");
  if(e.opmode_iso14443b)
    fprintf(stderr, "ISO14443B ");
  if(e.opmode_sr176)
    fprintf(stderr, "SR176 ");
  fprintf(stderr, "\n");

  fprintf(stderr, "  Single shot time-out value: %02X = %ims\n", e.single_shot_timeout, e.single_shot_timeout*100);

  fprintf(stderr, "  PCON2: %02X\n", e.pcon2);
  fprintf(stderr, "    disable multi-tag reset: %i\n", e.disable_multitag_reset);
  fprintf(stderr, "    disable startup message: %i\n", e.disable_startup_msg);
  fprintf(stderr, "    enable binary frame v2: %i\n", e.enable_binary_frame_v2);
  fprintf(stderr, "    noisy environment: %i\n", e.noisy_env);
  fprintf(stderr, "    reset recovery time multiplier: %i\n", e.reset_recovery_time_multiplier + 1);
  fprintf(stderr, "    enable ISO14443B anti-collision: %i\n", e.enable_iso14443b_anticollision);
  fprintf(stderr, "    disable ISO14443-4 error handling: %i\n", e.disable_iso14434_error_handling);

  fprintf(stderr, "  Reset Off Time: %02X = %ims\n", e.reset_off_time, e.reset_off_time);
  fprintf(stderr, "  Reset Recovery Time: %02X = %ims\n", e.reset_recovery_time, e.reset_recovery_time);
  fprintf(stderr, "  Application Family ID: %02X\n", e.application_familiy_id);
  fprintf(stderr, "  ISO 14443A Selection Time-out: %02X = %ius\n", e.iso14443a_selection_timeout, e.iso14443a_selection_timeout*300);
  fprintf(stderr, "  ISO 14443B Selection Time-out: %02X = %ius\n", e.iso14443b_selection_timeout, e.iso14443b_selection_timeout*300);
  fprintf(stderr, "  SR176 Selection Time-out: %02X = %ius\n", e.sr176_selection_timeout, e.sr176_selection_timeout*300);

  fprintf(stderr, "  PCON3: %02X\n", e.pcon3);
  fprintf(stderr, "    disable automatic ISO1443-4 timeouts: %i\n", e.disable_automatic_iso144434_timeouts);
  fprintf(stderr, "    page read: %i\n", e.page_read);
  fprintf(stderr, "    ReqA extendend id: %i\n", e.reqa_extendend_id);

  fprintf(stderr, "  Page Start: %02X\n", e.page_start);
  fprintf(stderr, "  Page Number: %02X\n", e.page_num);

  unsigned char userdata[0xF0-0x80];
  if(acg_readconfig(fd, userdata, 0x80, 0xF0) == 0)
    {
      int i;
      fprintf(stderr, "  user data:\n");
      for(i=0; i<sizeof(userdata);)
	{
	  int j;
	  fprintf(stderr, "    %02X: ", i+0x80);

	  for(j=0; j!=16 && i<sizeof(userdata); ++j, ++i)
	    fprintf(stderr, "%02X ", userdata[i]);

	  fprintf(stderr, " - ");
	  i-=j;

	  for(j=0; j!=16 && i<sizeof(userdata); ++j, ++i)
	    fprintf(stderr, "%c", isprint(userdata[i])?userdata[i]:'.');

	  fprintf(stderr, "\n");
	}
    }

  return 0;
}

int acg_setup_dual_2_2(int fd)
{
  fprintf(stderr, "Setup ACG DUAL 2.2\n");

  struct eeprom_dual_2_2 e;
  unsigned char *p=(unsigned char*)&e;
  if(acg_readconfig(fd, p, 0, sizeof(e)) < 0)
    {
      fprintf(stderr, "could not read eeprom config\n");
      return -1;
    }

  e.station_id=1;

  e.auto_start=0;
  e.protocol=0;
  e.multitag=0;
  e.new_serial_mode=1;
  e.led=0;
  e.single_shot=0;
  e.extended_protocol=1;
  e.extended_id=0;

  e.command_guard_time=0x20;

  e.opmode_iso14443a=1;
  e.opmode_iso14443b=1;
  e.opmode_sr176=1;

  e.single_shot_timeout=0x0A;

  e.disable_multitag_reset=0;
  e.disable_startup_msg=0;
  e.enable_binary_frame_v2=0;
  e.noisy_env=0;
  e.reset_recovery_time_multiplier=0;
  e.enable_iso14443b_anticollision=0;
  e.disable_iso14434_error_handling=0;

  e.reset_off_time=0x0A;
  e.reset_recovery_time=0x25;
  e.application_familiy_id=0;
  e.iso14443a_selection_timeout=0x10;
  e.iso14443b_selection_timeout=0x50;
  e.sr176_selection_timeout=0x10;

  e.disable_automatic_iso144434_timeouts=0;
  e.page_read=0;
  e.reqa_extendend_id=0;
  e.page_num=1;

  int skiplist[]={0x10,0x11,0x12,0x1D,0x1E,-1};
  if(acg_writeconfig(fd, p+0x0A, 0x0A, sizeof(e), skiplist) < 0)
    {
      fprintf(stderr, "error while writing reader config\n");
      return -1;
    }

  char *str="setup for rfdump";
  acg_writeconfig(fd, (unsigned char*)str, 0xE0, 0xE0+strlen(str), NULL);

  rfidReset(fd);
  return 0;
}

/***************************************************************************/

#pragma pack(push, 1)
struct eeprom_multiiso_1_0
{
  char device_id[5];		/* 00-04 */
  char admin_data[5];		/* 05-09 */
  unsigned char station_id;	/* 0A */

  union {
    unsigned char pcon;		/* 0B */
    struct {
      unsigned auto_start : 1;
      unsigned protocol : 1;
      unsigned multitag : 1;
      unsigned new_serial_mode : 1;
      unsigned led : 1;
      unsigned single_shot : 1;
      unsigned extended_protocol : 1;
      unsigned extended_id : 1;
    };
  };

  unsigned char baud_rate;	/* 0C */
  unsigned char command_guard_time; /* 0D */

  union {
    unsigned char operation_mode; /* 0E */
    struct {
      unsigned opmode_iso14443a : 1;
      unsigned opmode_iso14443b : 1;
      unsigned opmode_sr176 : 1;
      unsigned opmode_icode : 1;
      unsigned opmode_iso15693 : 1;
      unsigned opmode_icode_epc : 1;
      unsigned opmode_icode_uid : 1;
    };
  };

  unsigned char single_shot_timeout; /* 0F */
  unsigned char nix1[3];	/* 10-12 */

  union {
    unsigned char pcon2;	/* 13 */
    struct {
      unsigned disable_multitag_reset : 1;
      unsigned disable_startup_msg : 1;
      unsigned enable_binary_frame_v2 : 1;
      unsigned noisy_env : 1;
      unsigned reset_recovery_time_multiplier : 2;
      unsigned enable_iso14443b_anticollision : 1;
      unsigned disable_iso14434_error_handling : 1;
    };
  };

  unsigned char reset_off_time;	/* 14 */
  unsigned char reset_recovery_time; /* 15 */
  unsigned char application_familiy_id;	/* 16 */
  unsigned char iso14443a_selection_timeout; /* 17 */
  unsigned char iso14443b_selection_timeout; /* 18 */
  unsigned char sr176_selection_timeout; /* 19 */
  unsigned char iso15693_selection_timeout; /* 1A */

  union {
    unsigned char pcon3;	/* 1B */
    struct {
      unsigned disable_automatic_iso144434_timeouts : 1;
      unsigned rfu_1 : 1;
      unsigned page_read : 1;
      unsigned int_1 : 3;
      unsigned reqa_extendend_id : 1;
    };
  };

  unsigned char page_start;	/* 1C */
  unsigned char nix2[2];	/* 1D-1E */
  unsigned char page_num;	/* 1F */
};
#pragma pack(pop)

int acg_printinfo_multiiso_1_0(int fd)
{
  struct eeprom_multiiso_1_0 e;
  if(acg_readconfig(fd, (unsigned char*)&e, 0, sizeof(e)) < 0)
    {
      fprintf(stderr, "could not read eeprom config\n");
      return -1;
    }

  fprintf(stderr, "ACG MultiISO 1.0 EEPROM contents:\n");
  fprintf(stderr, "  device ID: %02X%02X%02X%02X%02X\n", e.device_id[0], e.device_id[1], e.device_id[2], e.device_id[3], e.device_id[4]);
  fprintf(stderr, "  admin data: %02X%02X%02X%02X%02X\n", e.admin_data[0], e.admin_data[1], e.admin_data[2], e.admin_data[3], e.admin_data[4]);
  fprintf(stderr, "  Station ID: %02X\n", e.station_id);

  fprintf(stderr, "  PCON1: %02X\n", e.pcon);
  fprintf(stderr, "    auto start: %i\n", e.auto_start);
  fprintf(stderr, "    protocol: %i = %s\n", e.protocol, e.protocol?"binary":"ASCII");
  fprintf(stderr, "    multitag: %i\n", e.multitag);
  fprintf(stderr, "    new serial mode: %i\n", e.new_serial_mode);
  fprintf(stderr, "    led: %i\n", e.led);
  fprintf(stderr, "    single shot: %i\n", e.single_shot);
  fprintf(stderr, "    extended protocol: %i\n", e.extended_protocol);
  fprintf(stderr, "    extended id: %i\n", e.extended_id);

  fprintf(stderr, "  Baud rate: %02X = ", e.baud_rate);
  switch(e.baud_rate & 7)
    {
    case 0: fprintf(stderr, "9600\n"); break;
    case 1: fprintf(stderr, "19200\n"); break;
    case 2: fprintf(stderr, "38400\n"); break;
    case 3: fprintf(stderr, "57600\n"); break;
    case 4: fprintf(stderr, "115200\n"); break;
    case 5: fprintf(stderr, "230400\n"); break;
    case 6: fprintf(stderr, "460800\n"); break;
    default: fprintf(stderr, "unknown: %i\n", e.baud_rate & 7); break;
    }

  fprintf(stderr, "  Command Guard Time: %02X = %.2fus\n", e.command_guard_time, 37.8f*e.command_guard_time);

  fprintf(stderr, "  Operation Mode: %02X = ", e.operation_mode);
  if(e.opmode_iso14443a)
    fprintf(stderr, "ISO14443A ");
  if(e.opmode_iso14443b)
    fprintf(stderr, "ISO14443B ");
  if(e.opmode_iso15693)
    fprintf(stderr, "ISO15693 ");
  if(e.opmode_sr176)
    fprintf(stderr, "SR176 ");
  if(e.opmode_icode)
    fprintf(stderr, "ICODE ");
  if(e.opmode_icode_epc)
    fprintf(stderr, "ICODE-EPC ");
  if(e.opmode_icode_uid)
    fprintf(stderr, "ICODE-UID ");
  fprintf(stderr, "\n");

  fprintf(stderr, "  Single shot time-out value: %02X = %ims\n", e.single_shot_timeout, e.single_shot_timeout*100);

  fprintf(stderr, "  PCON2: %02X\n", e.pcon2);
  fprintf(stderr, "    disable multi-tag reset: %i\n", e.disable_multitag_reset);
  fprintf(stderr, "    disable startup message: %i\n", e.disable_startup_msg);
  fprintf(stderr, "    enable binary frame v2: %i\n", e.enable_binary_frame_v2);
  fprintf(stderr, "    noisy environment: %i\n", e.noisy_env);
  fprintf(stderr, "    reset recovery time multiplier: %i\n", e.reset_recovery_time_multiplier + 1);
  fprintf(stderr, "    enable ISO14443B anti-collision: %i\n", e.enable_iso14443b_anticollision);
  fprintf(stderr, "    disable ISO14443-4 error handling: %i\n", e.disable_iso14434_error_handling);

  fprintf(stderr, "  Reset Off Time: %02X = %ims\n", e.reset_off_time, e.reset_off_time);
  fprintf(stderr, "  Reset Recovery Time: %02X = %ims\n", e.reset_recovery_time, e.reset_recovery_time);
  fprintf(stderr, "  Application Family ID: %02X\n", e.application_familiy_id);
  fprintf(stderr, "  ISO 14443A Selection Time-out: %02X = %ius\n", e.iso14443a_selection_timeout, e.iso14443a_selection_timeout*300);
  fprintf(stderr, "  ISO 14443B Selection Time-out: %02X = %ius\n", e.iso14443b_selection_timeout, e.iso14443b_selection_timeout*300);
  fprintf(stderr, "  SR176 Selection Time-out: %02X = %ius\n", e.sr176_selection_timeout, e.sr176_selection_timeout*300);
  fprintf(stderr, "  ISO 15693 Selection Time-out: %02X = %ius\n", e.iso15693_selection_timeout, e.iso15693_selection_timeout*300);

  fprintf(stderr, "  PCON3: %02X\n", e.pcon3);
  fprintf(stderr, "    disable automatic ISO1443-4 timeouts: %i\n", e.disable_automatic_iso144434_timeouts);
  fprintf(stderr, "    page read: %i\n", e.page_read);
  fprintf(stderr, "    ReqA extendend id: %i\n", e.reqa_extendend_id);

  fprintf(stderr, "  Page Start: %02X\n", e.page_start);
  fprintf(stderr, "  Page Number: %02X\n", e.page_num);

  unsigned char userdata[0xF0-0x80];
  if(acg_readconfig(fd, userdata, 0x80, 0xF0) == 0)
    {
      int i;
      fprintf(stderr, "  user data:\n");
      for(i=0; i<sizeof(userdata);)
	{
	  int j;
	  fprintf(stderr, "    %02X: ", i+0x80);

	  for(j=0; j!=16 && i<sizeof(userdata); ++j, ++i)
	    fprintf(stderr, "%02X ", userdata[i]);

	  fprintf(stderr, " - ");
	  i-=j;

	  for(j=0; j!=16 && i<sizeof(userdata); ++j, ++i)
	    fprintf(stderr, "%c", isprint(userdata[i])?userdata[i]:'.');

	  fprintf(stderr, "\n");
	}
    }

  return 0;
}

int acg_setup_multiiso_1_0(int fd)
{
  fprintf(stderr, "Setup ACG MultiISO 1.0\n");

  struct eeprom_multiiso_1_0 e;
  unsigned char *p=(unsigned char*)&e;
  if(acg_readconfig(fd, p, 0, sizeof(e)) < 0)
    {
      fprintf(stderr, "could not read eeprom config\n");
      return -1;
    }

  e.station_id=1;

  e.auto_start=0;
  e.protocol=0;
  e.multitag=0;
  e.new_serial_mode=1;
  e.led=0;
  e.single_shot=0;
  e.extended_protocol=1;
  e.extended_id=0;

  e.command_guard_time=0x20;

  e.operation_mode=0x7F;

  e.single_shot_timeout=0x0A;

  e.disable_multitag_reset=0;
  e.disable_startup_msg=0;
  e.enable_binary_frame_v2=0;
  e.noisy_env=0;
  e.reset_recovery_time_multiplier=0;
  e.enable_iso14443b_anticollision=0;
  e.disable_iso14434_error_handling=0;

  e.reset_off_time=0x0A;
  e.reset_recovery_time=0x25;
  e.application_familiy_id=0;
  e.iso14443a_selection_timeout=0x10;
  e.iso14443b_selection_timeout=0x50;
  e.sr176_selection_timeout=0x10;
  e.iso15693_selection_timeout=0x20;

  e.disable_automatic_iso144434_timeouts=0;
  e.page_read=0;
  e.reqa_extendend_id=0;
  e.page_num=1;

  int skiplist[]={0x10,0x11,0x12,0x1D,0x1E,-1};
  if(acg_writeconfig(fd, p+0x0A, 0x0A, sizeof(e), skiplist) < 0)
    {
      fprintf(stderr, "error while writing reader config\n");
      return -1;
    }

  char *str="setup for rfdump";
  acg_writeconfig(fd, (unsigned char*)str, 0xE0, 0xE0+strlen(str), NULL);

  rfidReset(fd);
  return 0;
}

/***************************************************************************/

#pragma pack(push, 1)
struct eeprom_iso_0_9
{
  char device_id[5];		/* 00-04 */
  char admin_data[5];		/* 05-09 */
  unsigned char station_id;	/* 0A */

  union {
    unsigned char pcon;		/* 0B */
    struct {
      unsigned auto_start : 1;
      unsigned protocol : 1;
      unsigned binary_timeout : 1;
      unsigned lock_mode : 1;
      unsigned led : 1;
      unsigned single_shot : 1;
      unsigned page_read : 1;
      unsigned extended_id : 1;
    };
  };

  unsigned char baud_rate;	/* 0C */
  unsigned char binary_watchdog_timer; /* 0D */

  union {
    unsigned char operation_mode; /* 0E */
    struct {
      unsigned opmode_iso15693 : 1;
      unsigned opmode_tagit : 1;
      unsigned opmode_icode : 1;
      unsigned opmode_iso14443a : 1;
      unsigned opmode_iso14443b : 1;
      unsigned opmode_sr176 : 1;
    };
  };

  unsigned char timeout_value;	/* 0F */
  unsigned char rfu;		/* 10 */
  unsigned char page_start;	/* 11 */
  unsigned char page_num;	/* 12 */
};
#pragma pack(pop)

int acg_printinfo_iso_0_9(int fd)
{
  struct eeprom_iso_0_9 e;
  if(acg_readconfig(fd, (unsigned char*)&e, 0, sizeof(e)) < 0)
    {
      fprintf(stderr, "could not read eeprom config\n");
      return -1;
    }

  fprintf(stderr, "ACG ISO 0.9 EEPROM contents:\n");
  fprintf(stderr, "  device ID: %02X%02X%02X%02X%02X\n", e.device_id[0], e.device_id[1], e.device_id[2], e.device_id[3], e.device_id[4]);
  fprintf(stderr, "  admin data: %02X%02X%02X%02X%02X\n", e.admin_data[0], e.admin_data[1], e.admin_data[2], e.admin_data[3], e.admin_data[4]);
  fprintf(stderr, "  Station ID: %02X\n", e.station_id);

  fprintf(stderr, "  PCON1: %02X\n", e.pcon);
  fprintf(stderr, "    auto start: %i\n", e.auto_start);
  fprintf(stderr, "    protocol: %i = %s\n", e.protocol, e.protocol?"binary":"ASCII");
  fprintf(stderr, "    binary timeout: %i\n", e.binary_timeout);
  fprintf(stderr, "    lock mode: %i\n", e.lock_mode);
  fprintf(stderr, "    led: %i\n", e.led);
  fprintf(stderr, "    single shot: %i\n", e.single_shot);
  fprintf(stderr, "    page read: %i\n", e.page_read);
  fprintf(stderr, "    extended id: %i\n", e.extended_id);

  fprintf(stderr, "  Baud rate: %02X = ", e.baud_rate);
  switch(e.baud_rate & 3)
    {
    case 0: fprintf(stderr, "9600\n"); break;
    case 1: fprintf(stderr, "19200\n"); break;
    case 2: fprintf(stderr, "38400\n"); break;
    case 3: fprintf(stderr, "57600\n"); break;
    default: fprintf(stderr, "unknown: %i\n", e.baud_rate & 7); break;
    }

  fprintf(stderr, "  binary watchdog timer: %02X\n", e.binary_watchdog_timer);

  fprintf(stderr, "  Operation Mode: %02X = ", e.operation_mode);
  if(e.opmode_iso15693)
    fprintf(stderr, "ISO15693 ");
  if(e.opmode_tagit)
    fprintf(stderr, "Tagit ");
  if(e.opmode_icode)
    fprintf(stderr, "Icode ");
  if(e.opmode_iso14443a)
    fprintf(stderr, "ISO14443A ");
  if(e.opmode_iso14443b)
    fprintf(stderr, "ISO14443B ");
  if(e.opmode_sr176)
    fprintf(stderr, "SR176 ");
  fprintf(stderr, "\n");

  fprintf(stderr, "  time-out value: %02X = %ims\n", e.timeout_value, e.timeout_value*100);

  fprintf(stderr, "  Page Start: %02X\n", e.page_start);
  fprintf(stderr, "  Page Number: %02X\n", e.page_num);

  unsigned char userdata[0xF0-0x20];
  if(acg_readconfig(fd, userdata, 0x20, 0xF0) == 0)
    {
      int i;
      fprintf(stderr, "  user data:\n");
      for(i=0; i<sizeof(userdata);)
	{
	  int j;
	  fprintf(stderr, "    %02X: ", i+0x20);

	  for(j=0; j!=16 && i<sizeof(userdata); ++j, ++i)
	    fprintf(stderr, "%02X ", userdata[i]);

	  fprintf(stderr, " - ");
	  i-=j;

	  for(j=0; j!=16 && i<sizeof(userdata); ++j, ++i)
	    fprintf(stderr, "%c", isprint(userdata[i])?userdata[i]:'.');

	  fprintf(stderr, "\n");
	}
    }

  return 0;
}

int acg_setup_iso_0_9(int fd)
{
  fprintf(stderr, "Setup ACG ISO 0.9\n");

  struct eeprom_iso_0_9 e;
  unsigned char *p=(unsigned char*)&e;
  if(acg_readconfig(fd, p, 0, sizeof(e)) < 0)
    {
      fprintf(stderr, "could not read eeprom config\n");
      return -1;
    }

  e.station_id=1;

  e.auto_start=0;
  e.protocol=0;
  e.binary_timeout=0;
  e.lock_mode=0;
  e.led=0;
  e.single_shot=0;
  e.page_read=0;
  e.extended_id=0;

  e.binary_watchdog_timer=0xFE;

  e.opmode_iso15693=1;
  e.opmode_tagit=1;
  e.opmode_icode=1;
  e.opmode_iso14443a=1;
  e.opmode_iso14443b=1;
  e.opmode_sr176=1;

  e.timeout_value=0x0A;
  e.page_start=0;
  e.page_num=1;

  int skiplist[]={0x10,0x11,0x12,-1};
  if(acg_writeconfig(fd, p+0x0A, 0x0A, sizeof(e), skiplist) < 0)
    {
      fprintf(stderr, "error while writing reader config\n");
      return -1;
    }

  char *str="setup for rfdump";
  acg_writeconfig(fd, (unsigned char*)str, 0xE0, 0xE0+strlen(str), NULL);

  rfidReset(fd);
  return 0;
}

/***************************************************************************/

#pragma pack(push, 1)
struct eeprom_lfx_1_0
{
  char device_id[5];		/* 00-04 */
  char admin_data[5];		/* 05-09 */
  unsigned char station_id;	/* 0A */

  //todo
  union {
    unsigned char pcon;		/* 0B */
    struct {
      unsigned auto_start : 1;
      unsigned protocol : 1;
      unsigned unused_1 : 1;
      unsigned lock_mode : 1;
      unsigned led : 1;
      unsigned single_shot : 1;
      unsigned page_read : 1;
      unsigned unused_2 : 1;
    };
  };

  unsigned char baud_rate;	/* 0C */
  unsigned char rfu1; /* 0D */

  //todo
  union {
    unsigned char operation_mode; /* 0E */
    struct {
      unsigned opmode_em4x02 : 1;
      unsigned opmode_em4x05 : 1;
      unsigned opmode_em4x50 : 1;
      unsigned opmode_hitag1 : 1;
      unsigned opmode_hitag2 : 1;
      unsigned opmode_q5 : 1;
      unsigned opmode_ti : 1;
      // rfu : 1
    };
  };

  unsigned char timeout_value;	/* 0F */
  unsigned char pcon2;		/* 10 */
  unsigned char page_start;	/* 11 */
  unsigned char page_num;	/* 12 */

  unsigned char rfu2; /* 13 */

  unsigned char reset_off_time; /* 14 */
  unsigned char reset_recovery_time; /* 15 */

  unsigned char em4x02_settings; /* 16 */
  unsigned char em4x05_settings; /* 17 */
  unsigned char em4x50_settings; /* 18 */
  unsigned char hitag1_settings; /* 19 */
  unsigned char hitag2_settings; /* 1a */
  unsigned char q5_settings; /* 1b */
};
#pragma pack(pop)

int acg_setup_lfx_1_0(int fd)
{
  fprintf(stderr, "Setup ACG LFX 1.0\n");

  struct eeprom_lfx_1_0 e;
  unsigned char *p=(unsigned char*)&e;
  if(acg_readconfig(fd, p, 0, sizeof(e)) < 0)
    {
      fprintf(stderr, "could not read eeprom config\n");
      return -1;
    }

  e.station_id=1;

  e.auto_start=0;
  e.protocol=0;
  e.lock_mode=0;
  e.led=0;
  e.single_shot=0;
  e.page_read=0;

  //e.baud_rate=0;

  e.opmode_em4x02=1;
  e.opmode_em4x05=1;
  e.opmode_em4x50=1;
  e.opmode_hitag1=1;
  e.opmode_hitag2=1;
  e.opmode_q5=1;
  e.opmode_ti=1;

  e.timeout_value=0x0A;
  e.pcon2=0;
  e.page_start=0;
  e.page_num=1;

  e.reset_off_time=0xA;
  e.reset_recovery_time=0xA;
  e.em4x02_settings=0x84;
  e.em4x05_settings=0x84;
  e.em4x50_settings=0x84;
  e.hitag1_settings=0x84;
  e.hitag2_settings=0x84;
  e.q5_settings=0x84;

  int skiplist[]={0xD,0x13,-1};
  if(acg_writeconfig(fd, p+0x0A, 0x0A, sizeof(e), skiplist) < 0)
    {
      fprintf(stderr, "error while writing reader config\n");
      return -1;
    }

  char *str="setup for rfdump";
  acg_writeconfig(fd, (unsigned char*)str, 0xE0, 0xE0+strlen(str), NULL);

  rfidReset(fd);
  return 0;
}

int acg_printinfo_lfx_1_0(int fd)
{
  struct eeprom_lfx_1_0 e;
  if(acg_readconfig(fd, (unsigned char*)&e, 0, sizeof(e)) < 0)
    {
      fprintf(stderr, "could not read eeprom config\n");
      return -1;
    }

  fprintf(stderr, "ACG LFX 1.0 EEPROM contents:\n");
  fprintf(stderr, "  device ID: %02X%02X%02X%02X%02X\n", e.device_id[0], e.device_id[1], e.device_id[2], e.device_id[3], e.device_id[4]);
  fprintf(stderr, "  admin data: %02X%02X%02X%02X%02X\n", e.admin_data[0], e.admin_data[1], e.admin_data[2], e.admin_data[3], e.admin_data[4]);
  fprintf(stderr, "  Station ID: %02X\n", e.station_id);

  fprintf(stderr, "  PCON1: %02X\n", e.pcon);
  fprintf(stderr, "    auto start: %i\n", e.auto_start);
  fprintf(stderr, "    protocol: %i = %s\n", e.protocol, e.protocol?"binary":"ASCII");
  fprintf(stderr, "    lock mode: %i\n", e.lock_mode);
  fprintf(stderr, "    led: %i\n", e.led);
  fprintf(stderr, "    single shot: %i\n", e.single_shot);
  fprintf(stderr, "    page read: %i\n", e.page_read);

  fprintf(stderr, "  Baud rate: %02X = ", e.baud_rate);
  switch(e.baud_rate & 3)
    {
    case 0: fprintf(stderr, "9600\n"); break;
    case 1: fprintf(stderr, "19200\n"); break;
    case 2: fprintf(stderr, "38400\n"); break;
    case 3: fprintf(stderr, "57600\n"); break;
    case 4: fprintf(stderr, "115200\n"); break;
    default: fprintf(stderr, "unknown: %i\n", e.baud_rate & 7); break;
    }

  fprintf(stderr, "  Operation Mode: %02X = ", e.operation_mode);
  if(e.opmode_em4x02)
    fprintf(stderr, "EM4x02 ");
  if(e.opmode_em4x05)
    fprintf(stderr, "EM4x05 ");
  if(e.opmode_em4x50)
    fprintf(stderr, "EM4x50 ");
  if(e.opmode_hitag1)
    fprintf(stderr, "HITAG1 ");
  if(e.opmode_hitag2)
    fprintf(stderr, "HITAG2 ");
  if(e.opmode_q5)
    fprintf(stderr, "Q5 ");
  if(e.opmode_ti)
    fprintf(stderr, "TI-RFID ");
  fprintf(stderr, "\n");

  fprintf(stderr, "  time-out value: %02X = %ims\n", e.timeout_value, e.timeout_value*100);

  fprintf(stderr, "  PCON2: %02X\n", e.pcon2);
  if(e.pcon2 & 1)
    fprintf(stderr, "    Compatibility Mode\n");
  if(e.pcon2 & 2)
    fprintf(stderr, "    Disable startup message\n");
  if(e.pcon2 & 4)
    fprintf(stderr, "    Enable binary frame v2\n");
  if(e.pcon2 & 8)
    fprintf(stderr, "    Noisy Environment\n");
  if(e.pcon2 & 16)
    fprintf(stderr, "    Safe Mode\n");

  fprintf(stderr, "  Page Start: %02X\n", e.page_start);
  fprintf(stderr, "  Page Number: %02X\n", e.page_num);
  fprintf(stderr, "  Reset Off Time: %ims\n", e.reset_off_time);
  fprintf(stderr, "  Reset Recovery Time: %ims\n", e.reset_recovery_time);

  fprintf(stderr, "  Tag Setting EM4x02: %02X\n", e.em4x02_settings);
  fprintf(stderr, "  Tag Setting EM4x05: %02X\n", e.em4x05_settings);
  fprintf(stderr, "  Tag Setting EM4x50: %02X\n", e.em4x50_settings);
  fprintf(stderr, "  Tag Setting HITAG1: %02X\n", e.hitag1_settings);
  fprintf(stderr, "  Tag Setting HITAG2: %02X\n", e.hitag2_settings);
  fprintf(stderr, "  Tag Setting Q5:     %02X\n", e.q5_settings);

  unsigned char userdata[0xF0-0x20];
  if(acg_readconfig(fd, userdata, 0x20, 0xF0) == 0)
    {
      int i;
      fprintf(stderr, "  user data:\n");
      for(i=0; i<sizeof(userdata);)
	{
	  int j;
	  fprintf(stderr, "    %02X: ", i+0x20);

	  for(j=0; j!=16 && i<sizeof(userdata); ++j, ++i)
	    fprintf(stderr, "%02X ", userdata[i]);

	  fprintf(stderr, " - ");
	  i-=j;

	  for(j=0; j!=16 && i<sizeof(userdata); ++j, ++i)
	    fprintf(stderr, "%c", isprint(userdata[i])?userdata[i]:'.');

	  fprintf(stderr, "\n");
	}
    }

  return 0;
}
