#include "s6400.h"
#include "rfid.h"

#include <aspoutil.h>

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

int s6400_serialishex = 0;

void s6400_prpkg(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[] = { 6, -2, 0 };
  LOG_IF( byte2hex(&end,(size_t *) &blen, pkg, pkg_len, blanks, 0) < 0, LOG_ERR) { }

  if (prefix) log_func(LOG_INFO, "%s %3lu <%s>\n", prefix, pkg_len, buf);
  else        printf("%3lu <%s>\n", pkg_len, buf);
}

int s6400_serial2str(uint32_t serial, char str[8]) {
  LOG_VIF( !str, LOG_ERR, " %s", "no output buffer") return -1;

  if (s6400_serialishex) {
    LOG_VIF( serial > 0xFFFFFFFL, LOG_ERR, " serial number (0x%X) won't fit in 7 digits", serial) return -1;
    sprintf(str, "%07X", serial);
  } else {
    LOG_VIF( serial >   9999999L, LOG_ERR, " serial number (%u) won't fit in 7 digits", serial) return -1;
    sprintf(str, "%07u", serial);
  }
  return 0;
}

int s6400_str2serial(void *str, uint32_t *serial) {
  int base = 10;
  char *buf = (char *) str;
  char *end = buf;
  char tmp[8];
  long val;

  *serial = 0;
  LOG_IF( str == NULL || !buf[0], LOG_ERR) return -1;

  if (s6400_serialishex) base = 16;
  LOG_IFERRNO((
    (val = strtol(buf, &end, base)) == LONG_MIN ||
    val == LONG_MAX ||
    val == 0 ) && errno, LOG_ERR) return -1;

  if (*end) {
    ERR("Strange characters in serial number <%s>", buf);
    return -1;
  }
  if (val < 0) {
    ERR("Negative serial number <%s>", buf);
    return -1;
  }
  *serial = (uint32_t) val;

  /* use the range checks in the other function */
  return s6400_serial2str(*serial, tmp);
}

/****************************************
 * page 22, 3.2.1.1 Request Packet Command Flags
 */
uint8_t s6400_default_flags = 0x03;

/****************************************
 * 3.2.4 BCC, page 25
 */
int s6400_addlrc( uint8_t *buf) {
  int length;
  int ix;
  uint8_t lrc = 0;
  int err = 0;

  LOG_IF(!buf, LOG_CRIT) return --err;

  LOG_IF(buf[0] != SOH, LOG_DEBUG) return --err;
  length = S6400_PACKETLENGTH(buf);
  LOG_IF( length < 8, LOG_CRIT ) return --err;
  for ( ix = 0; ix < length-2; ix++) lrc ^= buf[ix];
  buf[length-2] = lrc;
  buf[length-1] = ~lrc;

  return length;
}

/* 3.2.5 Packet reception and validation */
int s6400_validate( const uint8_t *buf, const size_t bufsize) {
  int length;
  int ix;
  uint8_t lrc = 0;
  int err = 0;

  LOG_IF(!buf || bufsize == 0, LOG_ERR) return -1;

  /* SOH */
  LOG_IF( buf[0] != SOH, LOG_DEBUG) goto s6400_validate_err;
  /* length 2 bytes */
  length = S6400_PACKETLENGTH(buf);
  LOG_IF( length > (int) bufsize || length < 8, LOG_DEBUG) goto s6400_validate_err;
  /* device id */
  LOG_IF( buf[3] != 0x10, LOG_DEBUG) goto s6400_validate_err;

  for ( ix = 0; ix < length-2; ix++) lrc ^= buf[ix];
  LOG_VIF( buf[length-2] != lrc, LOG_DEBUG, ", %d 0x%02X 0x%02X", length, buf[length-2], lrc) --err;
  LOG_VIF( buf[length-1] + lrc != 0xff, LOG_DEBUG, ", %d 0x%02X 0x%02X", length, buf[length-1], lrc) --err;
  if (err < 0) goto s6400_validate_err;

  return length;

 s6400_validate_err:
  s6400_prpkg(NULL, buf, bufsize);
  return -1;
}

/****************************************
 * page 26..58, ISO 15693 Commands
 */

/* page 27, 3.3.3.1 Read Single Block Command (20h) */
/* page 30, 3.3.3.2 Write Single Block (21h) */
/* page 33, 3.3.3.3 Lock Single Block Command (22h) */
/* page 36, 3.3.3.4 Read Multi-block Command (23h) */
/* page 39, 3.3.3.5 Write AFI Command (27h) */
/* page 42, 3.3.3.6 Lock AFI Command (28h) */
/* page 45, 3.3.3.7 Write DSFID Command (29h) */
/* page 48, 3.3.3.8 Lock DSFID Command (2Ah) */
/* page 51, 3.3.3.9 Read Tag Information Command (2Bh) */
const struct intstr s6400_data_flag[] = {
  { 0x01, "DSFID field present" },
  { 0x02, "AFI field present" },
  { 0x04, "VICC Memory size present" },
  { 0x08, "IC reference present" },
};

int s6400_read_tag_information_res(uint8_t *buf, uint8_t *data_flag, uint64_t *uid, uint8_t *dsfid, uint8_t *afi, uint8_t *nblocks, uint8_t *block_sz, uint8_t *ic_ref) {
  uint8_t *data;
  int ix;

  LOG_IF( !buf, LOG_ERR ) return -1;

  LOG_IF(S6400_CMDBYTE(buf) != 0x2B, LOG_ERR) return -1;
  /* TODO: this might not be true, does the "data flag" mean that a field be missing, pkg 1byte shorter? */
  LOG_VIF(S6400_PACKETLENGTH(buf) != 22, LOG_ERR, " a read tag information response is always 22 bytes long, got %d bytes", S6400_PACKETLENGTH(buf))
    return -1;

  data = buf+S6400_DATAOFFSET;
  if (data_flag) *data_flag = data[0];
  if (uid) {
    for (ix = 8; ix >= 1; ix-- ) {
      *uid <<= 8;
      *uid |= data[ix];
    }
  }

  ix = 8;
  if (data[0] & S6400_DATA_FLAG_DSFID) {
    if (dsfid)    *dsfid    = data[++ix]; /* usually  9 */
  }

  if (data[0] & S6400_DATA_FLAG_AFI) {
    if (afi)      *afi      = data[++ix]; /* usually 10 */
  }

  /* TODO: what does VICC mean, both or just one of them */
  if (data[0] & S6400_DATA_FLAG_VICC) {
    if (nblocks)  *nblocks  = data[++ix]; /* usually 11 */
    if (block_sz) *block_sz = data[++ix]; /* usually 12 */
  }

  if (data[0] & S6400_DATA_FLAG_IC) {
    if (ic_ref)   *ic_ref   = data[++ix]; /* usually 13 */
  }

  return 0;
}

/* page 54, 3.3.3.10 Read Multi-block Security Status Command (2Ch) */
/* page 57, 3.3.3.11 Inventory Command (01h) */

int s6400_inventory_res(uint8_t *buf, uint8_t *status, uint8_t *uid_cnt, size_t uid_sz, uint64_t *uid) {
  uint8_t *data = buf+S6400_DATAOFFSET;
  uint8_t Status;
  uint8_t Uid_cnt;
  int ix;

  LOG_IF( !buf, LOG_ERR ) return -1;
  LOG_IF(S6400_CMDBYTE(buf) != 0x01, LOG_ERR) return -1;

  LOG_IF( (Status = data[0]) != 0x00, LOG_NOTICE) return -1; /* what does this byte mean except 0x00 == success ? */

  Uid_cnt = data[1];
  LOG_VIF(S6400_PACKETLENGTH(buf) != (6+2+2 + Uid_cnt * 8), LOG_ERR,
	  " an inventory response is 6 bytes header + status+uid_cnt + the uids + rlc bytes long, got %d bytes",
	  S6400_PACKETLENGTH(buf))
    return -1;

  if (status) *status = Status;
  if (uid_cnt) *uid_cnt = Uid_cnt;
  if (uid && uid_sz > 0) {
    uint8_t lim = Uid_cnt;
    uint8_t *pp = data+2;

    if (uid_sz < lim) lim = uid_sz;
    for (ix = 0; ix < lim; ++ix, pp += 8) {
      (void) rfid_buf2uid(pp, &uid[ix]);
    }
  }
  

  return Uid_cnt;
}

/****************************************
 * page 59..60, 3.4 ISO 14443A Commands
 * page 59, 3.4.1 View UID (40h)
 */

/****************************************
 * page 61..82, 3.5 Reader Commands
 * page 61, 3.5.2.1 Reader Version Command (E0h)
 */

int s6400_reader_version_res(uint8_t *buf, uint16_t *version, uint8_t *prod_id, uint32_t *serial) {
  uint8_t *data;
  LOG_IF( !buf, LOG_ERR ) return -1;
  LOG_VIF(S6400_PACKETLENGTH(buf) != 20, LOG_ERR, " a reader version response is always 20 bytes long, got %d bytes", S6400_PACKETLENGTH(buf))
    return -1;
  LOG_IF(S6400_CMDBYTE(buf) != 0xE0, LOG_ERR) return -1;
  LOG_VIF(buf[17] != '\0', LOG_ERR, " %s", "reader serial number must end with nul byte") return -1;

  data = buf+S6400_DATAOFFSET;
  if (version) {
    *version = data[0] | data[1]<<8;
  }

  if (prod_id) {
    *prod_id = data[3];
  }

  if (serial) {
    LOG_IF( s6400_str2serial(data+4, serial), LOG_ERR ) return -1;
  }

  return 0;
}

/* page 64, 3.5.2.2 Reader Reset Command (E5h) */
/* page 65, 3.5.2.3 Reader Set-up Command (E7h) */

int s6400_reader_setup_res(uint8_t *buf, uint8_t *mode1, uint8_t *mode2, uint8_t *baud, uint8_t *afi, uint8_t *encr) {
  uint8_t *data;
  LOG_IF( !buf, LOG_ERR ) return -1;
  /* the examples says 14 bytes (p.67-77), but 0x0D == 13, and counting bytes also gives me 13 */
  LOG_VIF(S6400_PACKETLENGTH(buf) != 13, LOG_ERR, " a reader setup response is always 13 bytes long, got %d bytes", S6400_PACKETLENGTH(buf))
    return -1;
  LOG_IF(S6400_CMDBYTE(buf) != 0xE7, LOG_ERR) return -1;

  data = buf+S6400_DATAOFFSET;
  if (mode1) { *mode1 = data[0]; }
  if (mode2) { *mode2 = data[1]; }
  if (baud ) { *baud  = data[2]; }
  if (afi  ) { *afi   = data[3]; }
  if (encr ) { *encr  = data[4]; }

  return 0;
}

/* page 69, 3.5.2.4 Get Reader Information Command (EBh) */
/* page 70, 3.5.2.5 Set Custom DES Key (ECh) */
/* page 71, 3.5.2.6 Reader Mode (EDh) */
/* page 79, 3.5.2.7 Activate LED (EEh) */
/* page 80, 3.5.2.8 Activate Audio (EFh) (misspelled header "Active"?) */

/****************************************
 * page 81..82, 3.5.3 Error Codes
 */

/* examples: example 33 page 33, 15p35, 20p39, 22p40, 24p41, 26p42, 28p43, 30p44, 32p45, 34p46, 36p47, 38p48 */
/*  78p81 (first example: is given as an "0x02 error from reader", how come, is 0x06 a special "tag" error */
/*  that is reported as a "reader" error?) */

/* Note: page 25, 3.2.3.1 Response Packet Flags, Example 4 says: */
/*   "11 = Error from Reader" which is probably a typing error */
/* page 60 and page 81 says as below: */

const struct intstr s6400_err_flag[] = {
  { 0x00, "No Error" },
  { 0x01, "Error From Tag" },
  { 0x02, "Error From Reader" },
  { 0x03, "Reserved" },
};


/* page 24, if there is an error response from the tag or reader, */
/* the error code is a single byte data response with codes as below */

/* page 81, 3.5.3.1 Error Codes from the Transponder */
/* theese are the same as iec15693 response error codes (except 0x06) */
/* Re. code 0x11: 0x27 write afi, 0x28 lock afi, 0x29 write dsfied, and 0x2A lock dsfid has a */
/* special value (see p39, 42, 45, and 48 */

 const struct intstr s6400_err_transponder[] = {
  { 0x01, "Command not supported" },
  { 0x02, "Command not recognized" },
  { 0x03, "Command option not supported" },
  { 0x06, "Tag CRC" },
  { 0x0F, "Error with no information given or a specific error code is not supported" },
  { 0x10, "Specified block not available (does not exist)" },
  { 0x11, "Specified block already locked, cannot be locked again" },
  { 0x11, "AFI is already locked" },
  { 0x12, "Specified block locked, contents cannot be changed" },
  { 0x13, "Specified block was not successfully programmed" },
  { 0x14, "Specified block was not successfully locked" },
  /* 0xA0 .. 0xDF: Custom command error codes */
};

/* page 82, 3.5.3.2 Error Codes from the Reader */

const struct intstr s6400_err_reader[] = {
  { 0x00, "No error" },
  { 0x01, "Transponder not found" },
  { 0x02, "Command not supported" },
  { 0x03, "Packet LRC invalid" },
  { 0x05, "General write failure" },
  { 0x06, "Tag CRC" }, /* according to example 78, first line */
  { 0x0F, "Undefined error" },
};

int s6400_check_error_res(uint8_t *buf, uint16_t *err_code, const char **err1, const char **err2) {
  const uint8_t flag = S6400_FLAGBYTE(buf);
  const uint8_t code = buf[S6400_DATAOFFSET];
  int ix;

  *err_code = flag << 8;
  *err1 = NULL;
  *err2 = NULL;

  if (flag == 0) return 0;

  *err_code |= code;

  /* 3.2.3.1 page 1 */
  LOG_VIF( flag & 0xfc, LOG_ERR, " protocol error, valid values are 0x00 to 0x03 <%c>", flag) return -1;
  LOG_VIF( flag == 0x03, LOG_ERR, " protocol error, we don't accept 0x03 for now <%c>", flag) return -1;

  if ( (ix = intstr_find_code(flag, s6400_err_flag, sizeof(s6400_err_flag)/sizeof(struct intstr))) >= 0 ) {
    *err1 = s6400_err_flag[ix].str;
  } else {
    ERR(" unknown error code %02X:%02X", flag, code);
    return -1;
  }

  switch (flag) {
  case 1: /* error from transponder */
    if ( (ix = intstr_find_code(code, s6400_err_transponder, sizeof(s6400_err_transponder)/sizeof(struct intstr))) >= 0) {
      if (code == 0x11) { /* possible special case for write/lock afi/dsfid */
	uint8_t cmd = S6400_CMDBYTE(buf);
	if (0x27 <= cmd && cmd <= 0x2A) ix++; /* choose the second 0x11 alternative */
      }
      *err2 = s6400_err_transponder[ix].str;
    } else {
      ERR(" unknown error code %02X:%02X", flag, code);
      return -1;
    }
    break;

  case 2: /* error from reader */
    if ( (ix = intstr_find_code(code, s6400_err_reader, sizeof(s6400_err_reader)/sizeof(struct intstr))) >= 0) {
      *err2 = s6400_err_reader[ix].str;
    } else {
      ERR(" unknown error code %02X:%02X", flag, code);
      return -1;
    }
    break;

    default:
      ERR(" unknown error code %02X:%02X", flag, code);
      return -1;
      /*break; */
  }

  return 0; /* found error, but the error response is in itself legal */
}

int s6400_check_error_report(uint16_t err_code, const char *err1, const char *err2) {
  NOTICE("Error response packet %02X:%02X <%s:%s>", err_code>>8, err_code&0x0F, err1, err2);
  return 0;
}

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

/* all request builders ends up using this routine, they differ in how to set data/data_len */
int s6400_generic_req(uint8_t *buf, size_t bufsize, uint8_t cmd, uint32_t serial, uint64_t uid, uint8_t *data, size_t data_len) {
  size_t len = 8 + data_len + (serial ? 8: 0) + (uid ? 8: 0);
  uint8_t *pp = buf;
  size_t ix;

  LOG_VIF( !buf,                  LOG_CRIT, ", 0x%02X %d 0x%016lX", cmd, serial, uid) return -1;
  LOG_VIF( bufsize < len,         LOG_ERR , ", %u %u 0x%02X %d 0x%016" PRIX64, (unsigned) bufsize, (unsigned) len, cmd, serial, uid) return -1;
  LOG_VIF( len > S6400_MAXREQLEN, LOG_ERR , ", %u %u 0x%02X %d 0x%016" PRIX64, (unsigned) bufsize, (unsigned) len, cmd, serial, uid) return -1;

  *pp++ = SOH;			/* buf[0] */
  *pp++ = len & 0x00ff;		/* buf[1] */
  *pp++ = (len & 0xff00) >> 8;	/* buf[2] */
  *pp++ = 0x10;			/* buf[3] */
  *pp++ = 0x80 | (serial ? 0x20: 0) | (uid ? 0x10: 0) | (s6400_default_flags & 0x0F); /* buf[4] */
  *pp++ = cmd;			/* buf[5] */
  if (serial) {
    LOG_IF( s6400_serial2str(serial, (char *)pp), LOG_ERR) return -1;
    pp += 8;
 }
  if (uid) for (ix = 0; ix < 8; ++ix) { *pp++ = uid & 0xff; uid >>= 8; }
  if (data && data_len > 0) {
    uint8_t *qq = data;
    for (ix = 0; ix < data_len; ++ix) *pp++ = *qq++;
  }
  (void) s6400_addlrc(buf);
  pp += 2; /* for the two lrc bytes */
  /* cast below valid since len <= S6400_MAXREQLEN = 1008 */
  LOG_VIF( pp-buf != (ptrdiff_t) len, LOG_CRIT, ", 0x%02X %d 0x%016" PRIX64 " , %d != %d", cmd, serial, uid, (int) (pp-buf), (int) len) return -1;

  return len;
}

int s6400_generic1_req(uint8_t *buf, size_t bufsize, uint8_t cmd, uint32_t serial, uint64_t uid, uint8_t a) {
  uint8_t data[1];
  data[0] = a;
  return s6400_generic_req( buf, bufsize, cmd, serial, uid, data, 1);
}

int s6400_generic2_req(uint8_t *buf, size_t bufsize, uint8_t cmd, uint32_t serial, uint64_t uid, uint8_t a, uint8_t b) {
  uint8_t data[2];
  data[0] = a;
  data[1] = b;
  return s6400_generic_req( buf, bufsize, cmd, serial, uid, data, 2);
}

int s6400_generic3_req(uint8_t *buf, size_t bufsize, uint8_t cmd, uint32_t serial, uint64_t uid, uint8_t a, uint8_t b, uint8_t c) {
  uint8_t data[3];
  data[0] = a;
  data[1] = b;
  data[2] = c;
  /*printf("%02x %02x %02x\n", a, b, c); */
  return s6400_generic_req( buf, bufsize, cmd, serial, uid, data, 3);
}

struct intstr s6400_cmd[] = {
  { 0x20, "read_single_block" },
  { 0x21, "write_single_block" },
  { 0x22, "lock_single_block" },
  { 0x23, "read_multiblock" },
  { 0x27, "write_afi" },
  { 0x28, "lock_afi" },
  { 0x29, "write_dsfid" },
  { 0x2A, "lock_dsfid" },
  { 0x2B, "read_tag_information" },
  { 0x2C, "read_multiblock_security_status" },
  { 0x01, "inventory" },
  { 0x40, "view_uid" },
  { 0xE0, "reader_version" },
  { 0xE5, "reader_reset" },
  { 0xE7, "reader_setup" },
  { 0xEB, "get_reader_information" },
  { 0xEC, "set_custom_des_key" },
  { 0xED, "reader_mode" },
  /*{ 0xED, "reader_mode_wieg" }, */
  { 0xEE, "activate_led" },
  { 0xEF, "activate_audio" },
};

int s6400_strcmd(uint8_t *buf, size_t bufsize, int argc, char *argv[]) {
  uint8_t mode = 0;
  uint32_t serial = 0;
  uint64_t uid = 0;
  uint8_t data[S6400_MAXREQLEN - 8];
  size_t data_len = 0;
  uint8_t va = 0;
  uint8_t vb = 0;
  int ix;
  char *pp;
  uint8_t *qq;
  char *cmd;

  LOG_IF( !buf, LOG_CRIT ) return -1;
  LOG_IF( bufsize < S6400_MAXREQLEN, LOG_CRIT ) return -1;
  LOG_IF( argc < 5, LOG_ERR) return -1;

  memset(data, 0, S6400_MAXREQLEN - 8);


  ix = -1;
  if (argc > ++ix) LOG_IFERRNO( (mode = strtoul(argv[ix], &pp, 0), errno), LOG_CRIT ) return -1;
  if (argc > ++ix) cmd = argv[ix];
  if (argc > ++ix) LOG_IF( s6400_str2serial(argv[ix], &serial), LOG_CRIT ) return -1;
  if (argc > ++ix) LOG_IFERRNO( (uid = strtoull(argv[ix], &pp, 16), errno), LOG_CRIT ) return -1;
  if (argc > ++ix) {
    char *sta = argv[ix];
    int len = strlen(sta);
    short unsigned tmp;
    for ( pp = sta, qq = data; pp-sta < len && sscanf( pp, "%2hx", &tmp) != EOF; pp += 2, ++qq) {
      *qq = (uint8_t) tmp;
    }
    data_len = qq - data;
  }
  if (data_len > 0) va = data[0];
  if (data_len > 1) vb = data[1];

  /*printf("Mode: %02x Cmd: \"%s\" Serial: %07d Uid: %016llx\n", mode, cmd, serial, uid); */
  /*for (ix = 0; ix < data_len; ++ix) printf(" %02x", data[ix]); */
  /*printf("\n"); */
  /*printf("va: %02x vb: %02x\n", va, vb); */

  s6400_default_flags |= mode & 0x0f;

  if (!strcmp("read_single_block",cmd) || !strcmp("rsb",cmd))
    ix = s6400_read_single_block_req( buf, bufsize, serial, uid, va);
  else if (!strcmp("write_single_block",cmd) || !strcmp("wsb",cmd))
    ix = s6400_write_single_block_req( buf, bufsize, serial, uid, data, data_len);
  else if (!strcmp("lock_single_block",cmd) || !strcmp("lsb",cmd))
    ix = s6400_lock_single_block_req( buf, bufsize, serial, uid, va);
  else if (!strcmp("read_multiblock",cmd) || !strcmp("rm",cmd))
    ix = s6400_read_multiblock_req(buf, bufsize, serial, uid, va, vb);
  else if (!strcmp("write_afi",cmd) || !strcmp("wafi",cmd))
    ix = s6400_write_afi_req( buf, bufsize, serial, uid, va);
  else if (!strcmp("lock_afi",cmd) || !strcmp("lafi",cmd))
    ix = s6400_lock_afi_req( buf, bufsize, serial, uid);
  else if (!strcmp("write_dsfid",cmd) || !strcmp("wd",cmd))
    ix = s6400_write_dsfid_req( buf, bufsize, serial, uid, va);
  else if (!strcmp("lock_dsfid",cmd) || !strcmp("ld",cmd))
    ix = s6400_lock_dsfid_req(buf, bufsize, serial, uid);
  else if (!strcmp("read_tag_information",cmd) || !strcmp("rti",cmd))
    ix = s6400_read_tag_information_req(buf, bufsize, serial, uid);
  else if (!strcmp("read_multiblock_security_status",cmd) || !strcmp("rms",cmd))
    ix = s6400_read_multiblock_security_status_req(buf, bufsize, serial, uid, va, vb);
  else if (!strcmp("inventory",cmd) || !strcmp("i",cmd))
    ix = s6400_inventory_req( buf, bufsize, serial, uid, va);
  else if (!strcmp("view_uid",cmd) || !strcmp("vuid",cmd))
    ix = s6400_view_uid_req( buf, bufsize, serial);
  else if (!strcmp("reader_version",cmd) || !strcmp("rv",cmd))
    ix = s6400_reader_version_req(buf, bufsize, serial);
  else if (!strcmp("reader_reset",cmd) || !strcmp("rr",cmd))
    ix = s6400_reader_reset_req(buf, bufsize, serial);
  else if (!strcmp("reader_setup",cmd) || !strcmp("rs",cmd))
    ix = s6400_reader_setup_req(buf, bufsize, serial);
  else if (!strcmp("get_reader_information",cmd) || !strcmp("gri",cmd))
    ix = s6400_get_reader_information_req(buf, bufsize, serial);
  else if (!strcmp("set_custom_des_key",cmd) || !strcmp("des",cmd))
    ix = s6400_set_custom_des_key_req(buf, bufsize, serial, uid, data);
  else if (!strcmp("reader_mode",cmd) || !strcmp("mode",cmd))
    ix = s6400_reader_mode_req( buf, bufsize, serial, va, vb);
  else if (!strcmp("reader_mode_wieg",cmd) || !strcmp("modew",cmd))
    ix = s6400_reader_mode_wieg_req( buf, bufsize, serial, va, vb);
  else if (!strcmp("activate_led",cmd) || !strcmp("led",cmd))
    ix = s6400_activate_led_req(buf, bufsize, serial, va, vb);
  else if (!strcmp("activate_audio",cmd) || !strcmp("audio",cmd))
    ix = s6400_activate_audio_req( buf, bufsize, serial, va);
  else {
    ERR("No such command <%s>", cmd);
    ix = -1;
  }

  return ix;
}
