/***********************************************************************
 *
 * Copyrigth: Karl Hammar, Aspö Data, LGPL
 *
 */

#include "feig.h"

#include <aspoutil.h>

#include <ctype.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>

uint8_t feig_com_adr = 255;

uint16_t feig_crc_seed(uint8_t *buf, uint16_t len, uint16_t crc) {
  size_t ix;

  for (ix = 0; ix < len; ++ix) {
    uint8_t jx;
    crc ^= buf[ix];
    for (jx = 0; jx < 8; ++jx) {
      if (crc & 1) {
	crc = (crc >> 1) ^ FEIG_CRC_POLYNOM;
      } else {
	crc >>= 1;
      } 
    }
  }
  return crc;
}

uint16_t feig_add_crc(uint8_t *buf) {
  uint16_t crc;
  uint8_t len;

  if (!buf) return 0;
  len = buf[0];
  if (len < FEIG_MIN_STDPKT_LEN) return 0;

  crc = feig_crc(buf, len-2);
  crc = feig_crc(buf, len-2);
  buf[len-2] = crc & 0xff;
  buf[len-1] = crc >> 8;

  return crc;
}

int feig_verify_pkg( uint8_t *buf, const uint16_t sz, uint8_t *com_adr, uint8_t *cmd, uint8_t **data, uint16_t *data_len) {
  uint8_t *pp = buf;
  uint16_t len;

  if (!buf || sz < FEIG_MIN_STDPKT_LEN) return -1;

  if (*pp == STX) {
    len  = *(++pp) << 8;
    len |= *(++pp);
    if (data_len) *data_len = len - 7;
  } else {
    len = *pp;
    if (data_len) *data_len = len - 5;
  }
  LOG_IF( len > sz, LOG_ERR) return -2;
  if (com_adr) *com_adr = *(++pp);
  if (cmd)     *cmd     = *(++pp);
  if (data)    *data = ++pp;
  LOG_IF( feig_crc(buf,len), LOG_ERR) return -3;

  return len;
}

#define MKFEIG_BAUD( cnf, rate ) ( ((cnf) << 3*8) | ((rate) & 0xffffff) )
const uint32_t feig_baud[FEIG_BAUD_SZ] = {
  MKFEIG_BAUD( 0x05u,   4800u),
  MKFEIG_BAUD( 0x06u,   9600u),
  MKFEIG_BAUD( 0x07u,  19200u),
  MKFEIG_BAUD( 0x08u,  38400u),
  MKFEIG_BAUD( 0x09u,  57600u),
  MKFEIG_BAUD( 0x0Bu, 115200u),
  MKFEIG_BAUD( 0x0Du, 230400u),
  MKFEIG_BAUD( 0x80u, 100000u),
  MKFEIG_BAUD( 0x81u, 125000u),
  MKFEIG_BAUD( 0x82u, 250000u),
  MKFEIG_BAUD( 0x83u, 500000u),
};
#undef MKFEIG_BAUD

const int feig_baud_bits[] = {
#ifdef  B38400
  B38400,
#endif
#ifdef  B57600
  B57600,
#endif
#ifdef  B115200
  B115200,
#endif
#ifdef  B19200
  B19200,
#endif
#ifdef  B9600
  B9600,
#endif
#ifdef  B4800
  B4800,
#endif
#ifdef  B100000
  B100000,
#endif
  /* Theese below might not pass a max232 chip well */
#ifdef  B125000
  B125000,
#endif
#ifdef  B230400
  B230400,
#endif
#ifdef  B250000
  B250000,
#endif
#ifdef  B500000
  B500000,
#endif
};
const uint8_t feig_baud_bits_sz = sizeof(feig_baud_bits)/sizeof(int);

uint8_t  feig_baud_r2c( uint32_t rate ) {
  int8_t ix;

  for (ix = sizeof(feig_baud)/sizeof(uint32_t) - 1; ix >= 0; ++ix) {
    if (feig_baud_rate(ix) < rate) return feig_baud_cnf(ix);
  }
  return feig_baud_cnf(0);
}

uint32_t feig_baud_c2r( uint8_t  cnf  ) {
  int8_t ix;

  for (ix = sizeof(feig_baud)/sizeof(uint32_t) - 1; ix >= 0; ++ix) {
    if (feig_baud_cnf(ix) < cnf) return feig_baud_rate(ix);
  }
  return feig_baud_rate(0);
}


const struct intstr feig_command[FEIG_COMMAND_SZ] = {
  /* p. 61-65: 5. Commands for Reader Configuration */
  /* argument/parameter byte[s] in the comment */
  { 0x80, "Read Configuration" },        /* CFG-ADR */
  { 0x81, "Write Configuration" },       /* CFG-ADR CFG-REC[14] */
  { 0x82, "Save Configuration" },        /* CFG-ADR */
  { 0x83, "Set Default Configuration" }, /* CFG-ADR */
  { 0xA2, "Write Mifare Reader Keys" },  /* KEY-TYPE KEY-ADR KEY[6] */

  /* p. 66-79: 6. Command for Reader Control */
  { 0x52, "Baud Rate Detection" },       /* - */
  { 0x55, "Start Flash Loader" },        /* -, Note: pon reset needed */
  { 0x63, "CPU Reset" },                 /* - */
  { 0x65, "Get Software Version" },      /* - */
  { 0x66, "Get Reader Info" },           /* MODE */
  { 0x69, "RF Reset" },                  /* - */
  { 0x6A, "RF Output ON/OFF" },          /* RF_OUTPUT */
  { 0x71, "Set Output" },                /* OS OSF OS_TIME[2] OUT_TIME[2] */
  { 0x74, "Get Input" },                 /* - */

  /* p. 80-125: 7. ISO Host Commands for Transponder Communication */
  { 0xB0, "ISO Standard Host Commands" },     /* p. 80-108 command below as data */
  { 0xB2, "ISO14443 Special Host Commands" }, /* p.109-125 command below as data */

  /* p.126-136: 8. Special Commands for Transponder Communication */
  { 0xBF, "ISO15693 Transparent Command" },   /* p.126-128 MODE RSP_LENGTH[2] res[2] (opt[2]) REQUEST_DATA[n] */
  { 0xBD, "ISO14443A Transparent Command" },  /* p.129-132 MODE RSP_LENGTH[2] TIMEOUT CRC_INFO (REQ_BITS) REQUEST_DATA[n] */
  { 0xBE, "ISO14443B Transparent Command" },  /* p.133-136 0x00 RSP_LENGTH[2] TIMEOUT FRAME CRC_INFO REQUEST_DATA[n] */
};

/* p. 80-126: 7. ISO Host Commands for Transponder Communication */
const struct intstr feig_iso_command[FEIG_ISO_COMMAND_SZ] = { /* all theese are preceded by 0xB0 or 0xB2, see above */
  /* p.80-97: 7.1. [0xB0] ISO Standard Host Commands */
  { 0x01, "Inventory" },                           /* p. 80 MODE NTFC_TIME */
  { 0x23, "Read Multiple Blocks" },                /* p. 94 MODE (UID[8]) DB_ADR DB_N */
  { 0x24, "Write Multiple Blocks" },               /* p. 96 MODE (UID[8]) DB_ADR DB_N DB_SIZE DB[DB_N*DB_SIZE] */
  { 0x25, "Select" },                              /* p. 91 MODE (UID_LEN) UID[8 alt. UID_LEN] */

  /* p.98-107: 7.2. [0xB0] ISO 15693 Mandatory and Optional Host Commands */
  { 0x02, "Stay Quiet" },                          /* p. 98 MODE UID[8] */
  { 0x22, "Lock Multiple Blocks" },                /* p. 99 MODE (UID[8]) DB_ADR DB-N */
  { 0x26, "Reset to Ready" },                      /* p.101 MODE (UID[8]) */
  { 0x27, "Write AFI" },                           /* p.102 MODE (UID[8]) AFI */
  { 0x28, "Lock AFI" },                            /* p.103 MODE (UID[8]) */
  { 0x29, "Write DSFI" },                          /* p.100 MODE (UID[8]) DSFID */
  { 0x2A, "Lock DSFI" },                           /* p.104 MODE (UID[8]) */
  { 0x2B, "Get System Information" },              /* p.105 MODE (UID[8]) */
  { 0x2C, "Get Multiple Block Security Status" },  /* p.107 MODE (UID[8]) DB_ADR DB_N */

  /* p.108: 7.3. [0xB0] ISO 14443 Standard Host Commands */
  { 0xC0, "Halt" },                                /* p.108 MODE */

  /* p.109-125: 7.4. [0xB2] ISO14443 Special Host Commands */
  { 0x30, "Mifare Value Commands" },               /* p.110 MODE MF_CMD DB_ADR OP_VALUE[4] DEST_ADR */
  { 0xB0, "Authent Mifare" },                      /* p.113 MODE DB_ADR KEY_TYPE KEY_ADR */
  { 0xB1, "Authent my-d" },                        /* p.116 MODE (UID[8]) KEY_ADR_TAG AUTH_COUNTER_ADR AUTHENT_SEQUENCE KEY_ADR_SAM reserved[6] */
  { 0xBE, "ISO 14443-4 T=CL (#)" },                /* p.117 MODE DATA[n] */
  { 0xBF, "ISO 14443-4 Container Command (#)" },   /* p.116 RSP TIMEOUT_FWT REQUEST_BLOCK */
  { 0x2B, "ISO14443-4 Transponder-Info" },         /* p.125 - */
};

/* p. 157 */
const struct intstr feig_transponder_type[FEIG_TRANSPONDER_TYPE_SZ] = {
  { 0x00, "NXP I-Code1" },
  { 0x03, "Transponder according ISO15693" },
  { 0x04, "Transponder according ISO14443A" },
  { 0x05, "Transponder according ISO14443B" },
  { 0x06, "NXP I-Code EPC" },
  { 0x08, "Innovision Jewel" },
  { 0x0A, "STMicroelectronics SR176" },
  { 0x0B, "STMicroelectronics SRIx (SRI4K, SRIX4K, SRIX512)" },
};

/* p. 158 */
const struct intstr feig_reader_type[FEIG_READER_TYPE_SZ] = {
  /* are theese numbers decimal or hex, 0x50 = 80 = CPR.M02, decimal it seems */
  { 30, "ID ISC.M01" },
  { 31, "ID ISC.M02" },
  { 40, "ID ISC.LR100" },
  { 41, "ID ISC.LR200" },
  { 60, "ID ISC.PRH101-A (RS232 or Bluetooth)" },
  { 61, "ID ISC.PRH101-U (USB-Version)" },
  { 71, "ID ISC.PRH100­U (USB-Version)" },
  { 72, "ID ISC.PRH100" },
  { 73, "ID ISC.MR100­U (USB-Version)" },
  { 74, "ID ISC.MR100 / PR100" },
  { 75, "ID ISC.MR200-A / -E" },
  { 76, "ID ISC.MR101-A / PR101-A" },
  { 78, "ID ISC MR101-U / PR101-U" },
  { 80, "ID CPR.M02" },
  { 81, "ID CPR.02" },
  { 82, "ID CPR40.xx-Ux" },
  { 83, "ID CPR40.xx-Cx / -Ax" },
  { 84, "ID CPR.M03 (586/#)" },
  { 87, "ID CPR.04 / ID CPR.M04 (596/#)" },
  { 88, "ID CPR.04-USB (USB-Version; 596/#)" },
  { 91, "ID ISC.LRU1000" },
};

/* p.159-160 */

const struct intstr feig_status_byte[FEIG_STATUS_BYTE_SZ] = {
  /* General */
  { 0x00, "OK" },

/* Transponder Status */
  { 0x01, "No Transponder" },
  { 0x02, "Data False" },
  { 0x03, "Write-Error" },
  { 0x04, "Address-Error" },
  { 0x05, "Wrong Transponder-Type" },
  { 0x08, "Authent-Error" },
  { 0x0E, "General-Error" },
  { 0x83, "RF Communication Error" },
  { 0x93, "Data Buffer Overflow" },
  { 0x94, "More Data" },
  { 0x95, "ISO15693-Error" },
  { 0x96, "ISO14443-Error" },

  /* Parameter Status */
  { 0x10, "EEPROM-failure" },
  { 0x11, "Parameter-Range-Error" },

  /* Interface Status */
  { 0x80, "Unknown Command" },
  { 0x81, "Length-Error" },
  { 0x82, "Command (currently) not available" },

  /* Reader Status */
  { 0xF1, "Hardware Warning" },

  /* p.61 5.1. [0x80] Read Configuration */
  { 0x15, "Read of a reserved configuration block" },

  /* p.62 5.2. [0x81] Write Configuration */
  { 0x16, "Write to a reserved configuration block" },
};

int feig_check_status(uint8_t status, uint8_t print) {
  if (print == 0xff) return 0;
  if (status >= print) {
    int ix = intstr_find_code(status, feig_status_byte, FEIG_STATUS_BYTE_SZ);
    if (ix >= 0) NOTICE("received status byte 0x%02x <%s>", status, feig_status_byte[ix].str);
    else         NOTICE("received status byte 0x%02x (unknown code)", status);
    return 1;
  }
  return 0;
}

/* p. 160 */
const struct intstr feig_iso15693_error[FEIG_ISO15693_ERROR_SZ] = {
  { 0x01, "The command is not supported, i.e. the request code is not recognised" },
  { 0x02, "The command is not recognised, for example: a format error occured" },
  { 0x03, "The option is not supported" },
  { 0x0F, "Unknown error" },
  { 0x10, "The specified block is not available (doesn't exist)" },
  { 0x11, "The specified block is already locked and thus cannot be locked again" },
  { 0x12, "The specified block is locked and ist content cannot be changed" },
  { 0x13, "The specified block was not successfully programmed" },
  { 0x14, "The specified block was not successfully locked" },
  /* 0xA0 - 0xDF Custom command error codes */
  /* all others reserved for future use */
};

/* p. 161 */
const struct intstr feig_iso14433_error[FEIG_ISO14433_ERROR_SZ] = {
  { 0x01, "Lowlevel Error: CRC, Framing or EGT error" },
  { 0x02, "Timeout" },
  { 0x03, "Protocol error" },
  { 0x04, "block-no error (Chaining)" },
  { 0x05, "Insufficient power" },
};

int feig_cmd1_req(  uint8_t *buf, uint8_t bufsz, uint8_t cmd) {
  uint8_t len = 5;

  if (!buf || bufsz < len) return -len; /* be nice to user, tell him what bufsz to provide */
  buf[0] = len;
  buf[1] = feig_com_adr;
  buf[2] = cmd;
  (void) feig_add_crc(buf);

  return len;
}

int feig_cmd2_req(  uint8_t *buf, uint8_t bufsz, uint8_t cmd, uint8_t data) {
  uint8_t len = 6;

  if (!buf || bufsz < len) return -len; /* be nice to user, tell him what bufsz to provide */
  buf[0] = len;
  buf[1] = feig_com_adr;
  buf[2] = cmd;
  buf[3] = data;
  (void) feig_add_crc(buf);

  return len;
}

int feig_cmd2a_req( uint8_t *buf, uint8_t bufsz, uint8_t cmd, uint8_t data, uint8_t *arr, uint8_t sz) {
  uint8_t len = 6+sz;

  if (!buf || bufsz < len) return -len; /* be nice to user, tell him what bufsz to provide */

  buf[0] = len;
  buf[1] = feig_com_adr;
  buf[2] = cmd;
  buf[3] = data;
  if (arr) memcpy(buf+4, arr, sz);
  (void) feig_add_crc(buf);

  return len;
}

/* p. 61-64 */

/* p.65 */
int feig_write_mifare_keys_req( uint8_t *buf, uint8_t bufsz, uint8_t key_type, uint8_t key_adr, uint8_t key[6]) {
  uint8_t len = 13;

  if (!buf || bufsz < len) return -len; /* be nice to user, tell him what bufsz to provide */

  buf[0] = len;
  buf[1] = feig_com_adr;
  buf[2] = 0xA2;
  buf[3] = key_type ? 1 : 0;
  buf[4] = key_adr & 0x0f;
  memcpy(buf+5, key, 6);
  (void) feig_add_crc(buf);

  return len;
}
int feig_set_output( uint8_t *buf, uint16_t bufsz, uint16_t os, uint16_t osf, uint16_t os_time, uint16_t out_time ) {
  uint8_t len = 13;

  if (!buf || bufsz < len) return -len; /* be nice to user, tell him what bufsz to provide */

  buf[ 0] = len;
  buf[ 1] = feig_com_adr;
  buf[ 2] = 0x71;
  buf[ 3] = os >> 8; /* DOC BUG: is it high or low byte first?? */
  buf[ 4] = os & 0xff;
  buf[ 5] = osf >> 8;
  buf[ 6] = osf & 0xff;
  buf[ 7] = os_time >> 8;
  buf[ 8] = os_time & 0xff;
  buf[ 9] = out_time >> 8;
  buf[10] = out_time & 0xff;
  (void) feig_add_crc(buf);

  return len;
}

void feig_prtpkg(const char *prefix, const void *pkg, const size_t pkg_len) {
#define BUFSZ 200
  char buf[BUFSZ];
  char *end = buf;
  ssize_t blen = BUFSZ;
  const int blanks[] = { 3, -2, 0 };

  LOG_IF( byte2hex(&end,(size_t *) &blen, pkg, pkg_len, blanks, 0) < 0, LOG_ERR) return;
  if (prefix) NOTICE("%s %3lu <%s>\n", prefix, pkg_len, buf);
  else        NOTICE(" %3lu <%s>\n", pkg_len, buf);
}

int feig_strcmd_usage(void) {
  struct strcmd_t {
    const char *s;
    const char *w;
  } list[] = {
    { "rconf", "Read Configuration" },
    { "wconf", "Write Configuration" },
    { "sconf", "Save Configuration" },
    { "dconf", "Set Default Configuration" },
    { "wmrk", "Write Mifare Reader Keys" },
    { "brd", "Baud Rate Detection" },
    { "sfl", "Start Flash Loader" },
    { "cpurst", "CPU Reset" },
    { "ver", "Get Software Version" },
    { "info", "Get Reader Info" },
    { "rfrst", "RF Reset" },
    { "rfonoff", "RF Output ON/OFF" },
    { "set", "Set Output" },
    { "get", "Get Input" },
    { "iso", "ISO Standard Host Commands" },
    { "3sp", "ISO14443 Special Host Commands" },
    { "3tr", "ISO15693 Transparent Command" },
    { "atr", "ISO14443A Transparent Command" },
    { "btr", "ISO14443B Transparent Command" },
  };
  size_t ix;
  const size_t sz = sizeof(list) / sizeof(struct strcmd_t);

  printf("Usage: Test... com_adr cmd [data...]\n");
  printf("List of commands:\n");
  for (ix = 0; ix < sz; ++ix) {
    printf("     %s\t%s\n", list[ix].s, list[ix].w);
  }
  return -sz;
}
int feig_strcmd(uint8_t *buf, size_t bufsz, int argc, char *argv[]) {
  int ix;
  uint8_t data[FEIG_MAX_STDPKT_LEN];
  size_t data_len = 0;
  uint8_t va = 0;
  uint8_t vb = 0;
  uint8_t vc = 0;
  char *cmd;
  char *pp; /* points into argv[][] */
  uint8_t *qq; /* points into data[] */

  ix = -1;
  if (++ix < argc) LOG_IFERRNO( (feig_com_adr = strtoul(argv[ix], &pp, 16), errno) || *pp != '\0', LOG_CRIT ) return -1;
  if (++ix < argc) cmd = argv[ix];
  memset(data, 0, FEIG_MAX_STDPKT_LEN);
  for (qq = data; ++ix < argc; ) { /* put the rest of argv into data */
    char *sta = argv[ix];
    int len = strlen(sta);
    short unsigned tmp;
    for ( pp = sta; pp-sta < len && sscanf( pp, "%2hx", &tmp) != EOF; pp += 2, ++qq) {
      *qq = (uint8_t) tmp;
    }
    data_len = qq - data;
  }
  ix = -1;

  if (data_len > 0) va = data[0];
  if (data_len > 1) vb = data[1];
  if (data_len > 2) vc = data[2];

  LOG_IF( !buf, LOG_CRIT ) return -1;
  LOG_IF( bufsz < FEIG_MAX_STDPKT_LEN, LOG_CRIT ) return -1;
  LOG_IF( argc < 2, LOG_ERR) return -1;

  if (!strcmp("list",cmd))
    ix = feig_strcmd_usage();
  else if (!strcmp("rconf",cmd) || !strcmp("Read Configuration",cmd))
    ix = feig_read_conf_req( buf, bufsz, va, vb);
  else if (!strcmp("wconf",cmd) || !strcmp("Write Configuration",cmd))
    ix = feig_write_conf_req( buf, bufsz, va, vb, data+2);
  else if (!strcmp("sconf",cmd) || !strcmp("Save Configuration",cmd))
    ix = feig_save_conf_req( buf, bufsz, va, vb);
  else if (!strcmp("dconf",cmd) || !strcmp("Set Default Configuration",cmd))
    ix = feig_setdef_conf_req( buf, bufsz, va, vb, vc);
  else if (!strcmp("wmrk",cmd) || !strcmp("Write Mifare Reader Keys",cmd))
    ix = feig_write_mifare_keys_req( buf, bufsz, va, vb, data+2);
  else if (!strcmp("brd",cmd) || !strcmp("Baud Rate Detection",cmd))
    ix = feig_baud_rate_detection( buf, bufsz );
  else if (!strcmp("sfl",cmd) || !strcmp("Start Flash Loader",cmd))
    ix = feig_start_flash_loader( buf, bufsz );
  else if (!strcmp("cpurst",cmd) || !strcmp("CPU Reset",cmd))
    ix = feig_cpu_reset( buf, bufsz );
  else if (!strcmp("ver",cmd) || !strcmp("Get Software Version",cmd))
    ix = feig_get_software_version( buf, bufsz );
  else if (!strcmp("info",cmd) || !strcmp("Get Reader Info",cmd))
    ix = feig_get_reader_info( buf, bufsz, va);
  else if (!strcmp("rfrst",cmd) || !strcmp("RF Reset",cmd))
    ix = feig_rf_reset( buf, bufsz );
  else if (!strcmp("rfonoff",cmd) || !strcmp("RF Output ON/OFF",cmd))
    ix = feig_rf_output_power( buf, bufsz, va );
  else if (!strcmp("set",cmd) || !strcmp("Set Output",cmd)) {
    uint16_t os = BUFTO_U16(data);
    uint16_t osf = BUFTO_U16(data+2);
    uint16_t os_time = BUFTO_U16(data+4);
    uint16_t out_time = BUFTO_U16(data+6);
    ix = feig_set_output( buf, bufsz, os, osf, os_time, out_time);
  } else if (!strcmp("get",cmd) || !strcmp("Get Input",cmd))
    ix = feig_get_input( buf, bufsz );
  else if (!strcmp("iso",cmd) || !strcmp("ISO Standard Host Commands",cmd)) {}
  else if (!strcmp("3sp",cmd) || !strcmp("ISO14443 Special Host Commands",cmd)) {}
  else if (!strcmp("3tr",cmd) || !strcmp("ISO15693 Transparent Command",cmd)) {}
  else if (!strcmp("atr",cmd) || !strcmp("ISO14443A Transparent Command",cmd)) {}
  else if (!strcmp("btr",cmd) || !strcmp("ISO14443B Transparent Command",cmd)) {}
  else {
    printf("Unknown command \"%s\"\n", cmd);
    ix = feig_strcmd_usage();
  }

  return ix;
}

int feig_readerinfo_print(const char *prefix, struct feig_readerinfo_t *info) {
  int ix;
  const char empty[] = "";

  if (!prefix) prefix = empty;
  if (!info) return 0;

  printf("%ssw_rev: %5d\n", prefix, info->sw_rev);
  printf("%sd_rev : %5d\n", prefix, info->d_rev);

  ix = intstr_find_code(info->sw_type, feig_reader_type, FEIG_READER_TYPE_SZ);
  if (ix < 0) printf("%ssw_type:   %-2d (unknown)\n", prefix, info->sw_type);
  else {
    const char *suffix = "";
    if (info->sw_type == 80) {
      if (info->hw_type & BV(6)) suffix = "-B/-BA";
      else                       suffix = "-C/-CA";
    }
    printf("%ssw_type:   %-2d (%s %s)\n", prefix, info->sw_type, feig_reader_type[ix].str, suffix);
  }
  {
    const char *antenna = "external";
    const char *sam = "no sam socket";

    if (info->hw_type & BV(7)) antenna =  "internal";
    printf("%s with %s antenna (rf decoder type %d)\n", prefix, antenna, (info->hw_type >> 2) & 3);
    if (info->hw_type & BV(0)) {
      if (info->hw_type & BV(1)) sam = "SAM inserted";
      else sam = "no SAM inserted";
    }
    printf("%s with %s\n", prefix, sam);
  }
  {
    printf("%sTransponders supported:\n", prefix);
    for (ix = 0; ix < 8; ++ix) {
      if (info->tr_type & 1) {
	int code = intstr_find_code(ix, feig_transponder_type, FEIG_TRANSPONDER_TYPE_SZ);
	if (code < 0) printf("%s unknown (bit %d was set)\n", prefix, ix);
	else printf("%s %s\n", prefix, feig_transponder_type[code].str);
      }
      info->tr_type >>= 1;
    }
  }
  printf("%srx_buf: %d\n", prefix, info->rx_buf);
  printf("%stx_buf: %d\n", prefix, info->tx_buf);

  return 0;
}

int feig_get_reader_info_res(uint8_t *buf, uint16_t sz, const char *prefix, struct feig_readerinfo_t *info) {
  uint8_t com_adr;
  uint8_t cmd;
  uint8_t *data;
  uint16_t data_len;
  int ret;
  const int zlen = 17;
  const uint8_t zcmd = 0x66;
  struct feig_readerinfo_t x;

  if ( (ret = feig_verify_pkg(buf, sz, &com_adr, &cmd, &data, &data_len)) < 0) return ret;
  LOG_IF (ret != zlen || cmd != zcmd, LOG_NOTICE) return -1;

  /*feig_prtpkg(NULL, buf, data_len); */

  (void) feig_check_status(data[0], 1);
  if (data[0] > 0) return -1; /* i.e. not "OK" */

  if (!info) info = &x;
  /* this is the data payload: */
  info->sw_rev  = BUFTO_U16(buf+4);
  info->d_rev   = buf[6];
  info->hw_type = buf[7];
  info->sw_type = buf[8];
  info->tr_type = BUFTO_U16(buf+9);
  info->rx_buf  = BUFTO_U16(buf+11);
  info->tx_buf  = BUFTO_U16(buf+13);

  if (prefix) feig_readerinfo_print(prefix, info);

  return 0;
}

int feig_find(struct tty_open_save *save, struct termios *term, useconds_t sleep_time,
	      const char *prefix, struct feig_readerinfo_t *info) {
  uint8_t req[6];
  unsigned ibaud;
  int ipar;
  int len;
  uint8_t buf[BUFSZ];
  tcflag_t parity[] = { 0, PARENB | PARODD, PARENB };
  speed_t baudrate;

  (void) feig_get_reader_info(req, sizeof(req), 0);

  for (ibaud = 0; ibaud < feig_baud_bits_sz; ++ibaud ) {
    for (ipar = 2; ipar >= 0; --ipar) {
      const struct termiosBaud *bd;

      baudrate = feig_baud_bits[ibaud];
      LOG_IF( (bd = termiosBaud_speed(baudrate)) == NULL, LOG_ERR) continue;
      /*printf("Trying with %d, %s parity\n", bd->rate, parity_name[ipar]); */

      cfsetispeed( term, baudrate );
      cfsetospeed( term, baudrate );

      term->c_cflag &=  ~(PARENB | PARODD);
      term->c_cflag |= parity[ipar];

      LOG_VIFERRNO( tcsetattr( save->fd , TCSANOW, term ), LOG_ERR, " dev = %s, fd = %d", save->dev, save->fd ) {
	LOG_VIF(tty_close(save), LOG_ERR, " dev = %s, fd = %d", save->dev, save->fd) { }
	return -1;
      }

      LOG_IFERRNO( write(save->fd, req, 6) != 6, LOG_ERR) continue;
      usleep(sleep_time);
      LOG_IFERRNO( (len = read(save->fd, buf, BUFSZ)) < 0 && errno != EAGAIN, LOG_ERR) continue;
      if (errno == EAGAIN || len == 0) continue; /* no response */
      if (feig_get_reader_info_res(buf, BUFSZ, prefix, info) < 0) continue;

      return 0;
    }
  }

  return 0;
}

int feig_standard_setup(int fd, uint8_t sw_type, uint8_t com_adr) {
  /* default config according to doc. */
  uint8_t cfg[][14] = {
    /*  0     1     2     3     4     5     6     7     8     9    10    11    12    13 */
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, /* CFG0 p. */
    { 0x00, 0x00, 0x08, 0x01, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, /* CFG1 p. */
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, /* CFG2 p.32/37/39 */
    { 0x01, 0x38, 0x00, 0x0F, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, }, /* CFG3 p.40 */
    { 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, }, /* CFG4 p.45 */
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, }, /* CFG5 p.49 */
    { 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x05, 0x04, 0x00, }, /* CFG6 p.51 */
    { 0x02, 0x20, 0x2C, 0x01, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, /* CFG7 p.59 */
  };
  unsigned ix;
  /*int val; */
  const int sz = 20;
  uint8_t req[20];
  uint8_t res[20];
  uint8_t changed[] = { 7, 6, 1 };

  /*
  if (0) { / * T=CL p.40, TODO what is that? * /
    cfg[3][FEIG_CNF3_TAG_DRV_HI] = 0x00;
    cfg[3][FEIG_CNF3_TAG_DRV_LO] = 0x30;
  } else
  */
  /* this is just to init cfg[][] with defaults: */
  if (sw_type == 87 || sw_type == 88) { /* ID CPR.04 */
    cfg[3][FEIG_CNF3_TAG_DRV_HI]    = 0x00;
    cfg[3][FEIG_CNF3_TAG_DRV_LO]    = 0x30;
    cfg[3][FEIG_CNF3_ISO_BITRATE]   = 0xF0;
  } else if (sw_type == 81) { /* ID CPR.02 */
    cfg[2][FEIG_CNF2_SIGNAL_EVENTS] = 0x20;
    cfg[2][FEIG_CNF2_IDLE_STATE]    = 0x25;
    cfg[2][FEIG_CNF2_INPUT_EVENT]   = 0x04;
    cfg[2][FEIG_CNF2_OFFL_STATE]    = 0x2F;
    cfg[2][FEIG_CNF2_OFFL_FLASH]    = 0x0A;
    cfg[2][FEIG_CNF2_OFFL_TIME]     = 0x64;
  }

  /* theese are different from default */
  cfg[1][FEIG_CNF1_COM_ADR]     = com_adr;
  cfg[1][FEIG_CNF1_TRANSFORM]   = 0x00; /* no parity */
  cfg[1][FEIG_CNF1_READER_MODE] = 0x01; /* enable scan mode */
  cfg[6][FEIG_CNF6_SCAN_DATA1] |= BV(6); /* add com_addr in scan mode printout */
  cfg[7][FEIG_CFG7_SEP_CHAR]    = BV(6); /* let ' ' be the separator */
  cfg[7][FEIG_CFG7_SEP_USR]     = ':'; /* possible separator alternative */
  cfg[7][FEIG_CFG7_END_CHAR]    = BV(0); /* I think I favour \r\n */

  LOG_IF ( feig_setdef_conf_req(req, sz, 0, 1, 0) < 0, LOG_CRIT) return -1;
  LOG_IF ( feig_simple_io(fd, req, sz, res, sz, 500000) < 0, LOG_ERR) return -1;

  for (ix = 0; ix < sizeof(changed); ix++) {
    int num = changed[ix];
    LOG_IF ( feig_write_conf_req( req, sz, 0, num, cfg[num]) < 0, LOG_CRIT) return -1;
    LOG_IF ( feig_simple_io(fd, req, sz, res, sz, 300000) < 0, LOG_ERR) return -1;
  }

  LOG_IF ( feig_save_conf_req(req, sz, 1, 0) < 0, LOG_CRIT) return -1;
  LOG_IF ( feig_simple_io(fd, req, sz, res, sz, 500000) < 0, LOG_ERR) return -1;

  LOG_IF ( feig_cpu_reset(req, sz) < 0, LOG_CRIT) return -1;
  LOG_IF ( feig_simple_io(fd, req, sz, res, sz, 500000) < 0, LOG_ERR) return -1;

  return 0;
}

int feig_simple_io(int fd, const uint8_t *req, int reqsz, uint8_t *res, int ressz, useconds_t sleep_time) {
  int len;

  if (req) LOG_IFERRNO( write(fd, req, reqsz) < 0, LOG_ERR) return -1;
  if (req && res) usleep(sleep_time);
  if (res) {
    LOG_IFERRNO( (len = read(fd, res, ressz)) < 0, LOG_ERR) return -1;
    if (len == 0) return 0;
    if (feig_verify_pkg(res, len, NULL, NULL, NULL, NULL) < 0) return -len;
    if (feig_check_status(res[3], 2) < 0) return -len;
  }

  return len;
}

struct termios *termios_feig(struct termios *term) {
  speed_t baud_bits = B38400;

  term->c_iflag = ( IGNBRK | IGNPAR );
  term->c_oflag = 0;
  term->c_cflag = ( CLOCAL | CREAD | CS8 );
  term->c_lflag = 0;

  cfsetispeed( term, baud_bits );
  cfsetospeed( term, baud_bits );

  termiosCharClr(term);
  term->c_cc[VMIN]     = 1;
  term->c_cc[VTIME]    = 0;

  return term;
}

  /*
  for (ix = 7; ix > 0; ix++) { / * don't touch cfg[0] it is reserved * /
  }


  int ix;
  uint8_t cmd;
  uint8_t *data;
  uint16_t data_len;
  int len;
  int ret;

  LOG_IFERRNO( (ret = write(fd, buf, len)) == -1 || ret != len, LOG_ERR ) goto End;
  printf("write len = %d\n", ret);
  memset( buf, 0, BUFSZ);
  ret = 0;
  while (1) {
  usleep(200000);
  LOG_IFERRNO( (ret = read(fd, buf, BUFSZ)) == -1, LOG_ERR ) goto End;
  printf("read len = %d\n", ret);
  if (ret > 0) break;
  }

  if (feig_verify_pkg(buf, ret, &com_adr, &cmd, &data, &data_len) > 0) {
  feig_prtpkg(NULL, buf, ret);
  printf("adr cmd data_len, data: %02x %02x %d:", com_adr, cmd, data_len);
  feig_check_status(data[0], 0);
  } else {
  printf("Strange input:");
  for (ix = 0; ix < ret; ++ix) printf(" %02x", buf[ix]);
  printf("\n");
  }
  */

