proxmark3/armsrc/felica.c

901 lines
31 KiB
C
Raw Normal View History

2022-01-06 09:19:46 +08:00
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// 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 3 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.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
#include "felica.h"
#include "proxmark3_arm.h"
2017-10-21 02:30:17 +08:00
#include "BigBuf.h"
#include "util.h"
2017-12-24 17:30:29 +08:00
#include "protocols.h"
#include "crc16.h"
#include "fpgaloader.h"
#include "string.h"
#include "commonutil.h"
#include "dbprint.h"
#include "ticks.h"
#include "iso18.h"
// FeliCa timings
2019-08-06 19:51:10 +08:00
// minimum time between the start bits of consecutive transfers from reader to tag: 6800 carrier (13.56MHz) cycles
#ifndef FELICA_REQUEST_GUARD_TIME
2019-10-21 00:28:07 +08:00
# define FELICA_REQUEST_GUARD_TIME (6800/16 + 1) // 426
#endif
// FRAME DELAY TIME 2672 carrier cycles
#ifndef FELICA_FRAME_DELAY_TIME
2019-10-21 00:28:07 +08:00
# define FELICA_FRAME_DELAY_TIME (2672/16 + 1) // 168
#endif
#ifndef DELAY_AIR2ARM_AS_READER
2019-10-21 00:28:07 +08:00
#define DELAY_AIR2ARM_AS_READER (3 + 16 + 8 + 8*16 + 4*16 - 8*16) // 91
#endif
#ifndef DELAY_ARM2AIR_AS_READER
2019-10-21 00:28:07 +08:00
#define DELAY_ARM2AIR_AS_READER (4*16 + 8*16 + 8 + 8 + 1) // 209
#endif
2020-01-22 01:07:18 +08:00
#define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len))
2018-02-01 22:22:01 +08:00
static uint32_t felica_timeout;
static uint32_t felica_nexttransfertime;
static uint32_t felica_lasttime_prox2air_start;
2019-01-09 19:05:29 +08:00
static void iso18092_setup(uint8_t fpga_minor_mode);
static uint8_t felica_select_card(felica_card_select_t *card);
2023-08-09 14:55:20 +08:00
static void TransmitFor18092_AsReader(const uint8_t *frame, uint16_t len, const uint32_t *NYI_timing_NYI, uint8_t power, uint8_t highspeed);
static bool WaitForFelicaReply(uint16_t maxbytes);
2017-10-21 02:30:17 +08:00
2020-05-10 22:59:38 +08:00
static void iso18092_set_timeout(uint32_t timeout) {
2019-03-10 07:00:59 +08:00
felica_timeout = timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / (16 * 8) + 2;
}
2017-10-21 02:30:17 +08:00
2020-05-10 22:59:38 +08:00
static uint32_t iso18092_get_timeout(void) {
2019-03-10 07:00:59 +08:00
return felica_timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / (16 * 8) - 2;
}
2019-01-09 19:05:29 +08:00
#ifndef FELICA_MAX_FRAME_SIZE
2019-03-10 07:00:59 +08:00
#define FELICA_MAX_FRAME_SIZE 260
#endif
2017-10-21 02:30:17 +08:00
//structure to hold outgoing NFC frame
2019-03-10 07:00:59 +08:00
static uint8_t frameSpace[FELICA_MAX_FRAME_SIZE + 4];
2017-10-21 02:30:17 +08:00
//structure to hold incoming NFC frame, used for ISO/IEC 18092-compatible frames
static struct {
enum {
STATE_UNSYNCD,
STATE_TRYING_SYNC,
STATE_GET_LENGTH,
STATE_GET_DATA,
STATE_GET_CRC,
STATE_FULL
} state;
uint16_t shiftReg; //for synchronization and offset calculation
int posCnt;
2019-03-10 03:34:41 +08:00
bool crc_ok;
2017-10-21 02:30:17 +08:00
int rem_len;
uint16_t len;
uint8_t byte_offset;
2019-03-10 03:34:41 +08:00
uint8_t *framebytes;
2019-03-10 07:00:59 +08:00
//should be enough. maxlen is 255, 254 for data, 2 for sync, 2 for crc
// 0,1 -> SYNC, 2 - len, 3-(len+1)->data, then crc
2019-01-07 03:42:51 +08:00
} FelicaFrame;
2017-10-21 02:30:17 +08:00
//b2 4d is SYNC, 45645 in 16-bit notation, 10110010 01001101 binary. Frame will not start filling until this is shifted in
//bit order in byte -reverse, I guess? [((bt>>0)&1),((bt>>1)&1),((bt>>2)&1),((bt>>3)&1),((bt>>4)&1),((bt>>5)&1),((bt>>6)&1),((bt>>7)&1)] -at least in the mode that I read those in
#ifndef SYNC_16BIT
2019-01-07 03:42:51 +08:00
# define SYNC_16BIT 0xB24D
#endif
2017-10-21 02:30:17 +08:00
2020-05-10 22:59:38 +08:00
static void FelicaFrameReset(void) {
2019-01-07 03:42:51 +08:00
FelicaFrame.state = STATE_UNSYNCD;
FelicaFrame.posCnt = 0;
FelicaFrame.crc_ok = false;
FelicaFrame.byte_offset = 0;
}
static void FelicaFrameinit(uint8_t *data) {
2019-03-10 03:34:41 +08:00
FelicaFrame.framebytes = data;
FelicaFrameReset();
2017-10-21 02:30:17 +08:00
}
//shift byte into frame, reversing it at the same time
static void shiftInByte(uint8_t bt) {
2019-03-10 03:34:41 +08:00
uint8_t j;
2019-03-10 07:00:59 +08:00
for (j = 0; j < FelicaFrame.byte_offset; j++) {
FelicaFrame.framebytes[FelicaFrame.posCnt] = (FelicaFrame.framebytes[FelicaFrame.posCnt] << 1) + (bt & 1);
2017-10-21 02:30:17 +08:00
bt >>= 1;
}
2019-01-07 03:42:51 +08:00
FelicaFrame.posCnt++;
FelicaFrame.rem_len--;
2019-03-10 07:00:59 +08:00
for (j = FelicaFrame.byte_offset; j < 8; j++) {
FelicaFrame.framebytes[FelicaFrame.posCnt] = (FelicaFrame.framebytes[FelicaFrame.posCnt] << 1) + (bt & 1);
2017-10-21 02:30:17 +08:00
bt >>= 1;
}
}
static void Process18092Byte(uint8_t bt) {
2019-01-07 03:42:51 +08:00
switch (FelicaFrame.state) {
2019-03-10 03:34:41 +08:00
case STATE_UNSYNCD: {
//almost any nonzero byte can be start of SYNC. SYNC should be preceded by zeros, but that is not always the case
2019-03-10 03:34:41 +08:00
if (bt > 0) {
FelicaFrame.shiftReg = reflect8(bt);
FelicaFrame.state = STATE_TRYING_SYNC;
}
break;
}
case STATE_TRYING_SYNC: {
if (bt == 0) {
//desync
FelicaFrame.shiftReg = bt;
FelicaFrame.state = STATE_UNSYNCD;
} else {
2019-03-10 07:00:59 +08:00
for (uint8_t i = 0; i < 8; i++) {
2019-03-10 03:34:41 +08:00
if (FelicaFrame.shiftReg == SYNC_16BIT) {
//SYNC done!
FelicaFrame.state = STATE_GET_LENGTH;
FelicaFrame.framebytes[0] = 0xb2;
FelicaFrame.framebytes[1] = 0x4d;
FelicaFrame.byte_offset = i;
//shift in remaining byte, slowly...
2019-03-10 07:00:59 +08:00
for (uint8_t j = i; j < 8; j++) {
2019-03-10 03:34:41 +08:00
FelicaFrame.framebytes[2] = (FelicaFrame.framebytes[2] << 1) + (bt & 1);
bt >>= 1;
}
FelicaFrame.posCnt = 2;
2019-03-10 07:00:59 +08:00
if (i == 0)
2019-03-10 03:34:41 +08:00
break;
}
FelicaFrame.shiftReg = (FelicaFrame.shiftReg << 1) + (bt & 1);
bt >>= 1;
}
//that byte was last byte of sync
if (FelicaFrame.shiftReg == SYNC_16BIT) {
//Force SYNC on next byte
FelicaFrame.state = STATE_GET_LENGTH;
FelicaFrame.framebytes[0] = 0xb2;
FelicaFrame.framebytes[1] = 0x4d;
FelicaFrame.byte_offset = 0;
FelicaFrame.posCnt = 1;
}
}
break;
}
case STATE_GET_LENGTH: {
shiftInByte(bt);
FelicaFrame.rem_len = FelicaFrame.framebytes[2] - 1;
FelicaFrame.len = FelicaFrame.framebytes[2] + 4; //with crc and sync
FelicaFrame.state = STATE_GET_DATA;
break;
}
case STATE_GET_DATA: {
shiftInByte(bt);
if (FelicaFrame.rem_len <= 0) {
FelicaFrame.state = STATE_GET_CRC;
FelicaFrame.rem_len = 2;
}
break;
}
case STATE_GET_CRC: {
shiftInByte(bt);
2019-03-10 07:00:59 +08:00
if (FelicaFrame.rem_len <= 0) {
FelicaFrame.rem_len = 0;
2019-03-10 03:34:41 +08:00
// skip sync 2bytes. IF ok, residue should be 0x0000
2019-03-10 07:00:59 +08:00
FelicaFrame.crc_ok = check_crc(CRC_FELICA, FelicaFrame.framebytes + 2, FelicaFrame.len - 2);
2019-03-10 03:34:41 +08:00
FelicaFrame.state = STATE_FULL;
}
break;
}
case STATE_FULL: //ignore byte. Don't forget to clear frame to receive next one...
default:
break;
}
2017-10-21 02:30:17 +08:00
}
/* Perform FeliCa polling card
* Currently does NOT do any collision handling.
* It expects 0-1 cards in the device's range.
* return 0 if selection was successful
*/
static uint8_t felica_select_card(felica_card_select_t *card) {
2017-10-21 02:30:17 +08:00
2019-03-10 03:34:41 +08:00
// POLL command
// 0xB2 0x4B = sync code
// 0x06 = len
// 0x00 = rfu
// 0xff = system code service
// 0xff = system code service
// 0x00 = request code
2019-10-17 19:48:34 +08:00
// b7 = automatic switching of data rate
// b6-b2 = reserved
// b1 = fc/32 (414kbps)
// b0 = fc/64 (212kbps)
2019-03-10 03:34:41 +08:00
// 0x00 = timeslot
// 0x09 0x21 = crc
2019-03-10 07:00:59 +08:00
static uint8_t poll[10] = {0xb2, 0x4d, 0x06, FELICA_POLL_REQ, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x21};
int len = 10;
2019-03-10 03:34:41 +08:00
// We try 10 times, or if answer was received.
2019-03-10 03:34:41 +08:00
do {
// end-of-reception response packet data, wait approx. 501μs
// end-of-transmission command packet data, wait approx. 197μs
// polling card
TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0);
// polling card, break if success
if (WaitForFelicaReply(1024) && FelicaFrame.framebytes[3] == FELICA_POLL_ACK)
2019-03-10 03:34:41 +08:00
break;
WDT_HIT();
} while (--len);
// timed-out
2019-10-17 19:48:34 +08:00
if (len == 0) {
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG)
Dbprintf("Error: Time out card selection!");
2019-03-10 03:34:41 +08:00
return 1;
}
2019-03-10 03:34:41 +08:00
// wrong answer
2019-10-17 19:48:34 +08:00
if (FelicaFrame.framebytes[3] != FELICA_POLL_ACK) {
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG)
2019-10-17 19:48:34 +08:00
Dbprintf("Error: Wrong answer selecting card!");
2019-03-10 03:34:41 +08:00
return 2;
}
2019-03-10 03:34:41 +08:00
// VALIDATE CRC residue is 0, hence if crc is a value it failed.
2019-10-17 19:48:34 +08:00
if (!check_crc(CRC_FELICA, FelicaFrame.framebytes + 2, FelicaFrame.len - 2)) {
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Error: CRC check failed!");
Dbprintf("CRC check was done on Frame: ");
Dbhexdump(FelicaFrame.len - 2, FelicaFrame.framebytes + 2, 0);
}
2019-03-10 03:34:41 +08:00
return 3;
}
2019-03-10 03:34:41 +08:00
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG)
Dbprintf("Card selection successful!");
2019-03-10 03:34:41 +08:00
// copy UID
// idm 8
if (card) {
2023-08-09 14:55:20 +08:00
memcpy(card->IDm, FelicaFrame.framebytes + 4, 8);
2019-03-10 03:34:41 +08:00
memcpy(card->PMm, FelicaFrame.framebytes + 4 + 8, 8);
//memcpy(card->servicecode, FelicaFrame.framebytes + 4 + 8 + 8, 2);
2023-08-09 14:55:20 +08:00
memcpy(card->code, card->IDm, 2);
memcpy(card->uid, card->IDm + 2, 6);
memcpy(card->iccode, card->PMm, 2);
memcpy(card->mrt, card->PMm + 2, 6);
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Received Frame: ");
Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0);
}
2019-03-10 03:34:41 +08:00
}
// more status bytes?
return 0;
2017-10-21 02:30:17 +08:00
}
// poll-0: 0xb2,0x4d,0x06,0x00,0xff,0xff,0x00,0x00,0x09,0x21,
// resp: 0xb2,0x4d,0x12,0x01,0x01,0x2e,0x3d,0x17,0x26,0x47,0x80,0x95,0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00,0xb3,0x7f,
// poll-1 (reply with available system codes - NFC Tag3 specs, IIRC): 0xb2,0x4d,0x06,0x00,0xff,0xff,0x01,0x00,0x3a,0x10
// resp: 0xb2,0x4d,0x14,0x01, 0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX, 0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00, 0x88,0xb4,0x0c,0xe2,
// page-req: 0xb2,0x4d,0x10,0x06, 0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX, 0x01, 0x0b,0x00, 0x01, 0x80,0x00, 0x2e,0xb3,
// page-req: 0x06, IDm(8), ServiceNum(1),Slist(2*num) BLocknum (1) BLockids(2-3*num)
2017-10-21 02:30:17 +08:00
// page-resp: 0xb2,0x4d,0x1d,0x07, 0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX, 0x00, 0x00, 0x01, 0x10,0x04,0x01,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x23, 0xcb,0x6e,
// builds a readblock frame for felica lite(s). Using SERVICE: SERVICE_FELICA_LITE_READONLY
// Felica standard has a different file system, AFAIK,
// 8-byte IDm, number of blocks, blocks numbers
2017-10-21 02:30:17 +08:00
// number of blocks limited to 4 for FelicaLite(S)
2022-02-27 22:00:50 +08:00
static void BuildFliteRdblk(const uint8_t *idm, uint8_t blocknum, const uint16_t *blocks) {
2023-01-15 08:58:57 +08:00
if (blocknum > 4 || blocknum == 0)
2019-01-09 19:05:29 +08:00
Dbprintf("Invalid number of blocks, %d != 4", blocknum);
uint8_t c = 0, i = 0;
2020-01-22 01:07:18 +08:00
// Sync bytes
2017-11-11 08:26:53 +08:00
frameSpace[c++] = 0xb2;
frameSpace[c++] = 0x4d;
2019-03-10 03:34:41 +08:00
c++; //set length later
2019-03-10 03:34:41 +08:00
frameSpace[c++] = FELICA_RDBLK_REQ; //command number
2019-03-10 03:34:41 +08:00
//card IDm, from poll
frameSpace[c++] = idm[0];
frameSpace[c++] = idm[1];
frameSpace[c++] = idm[2];
frameSpace[c++] = idm[3];
frameSpace[c++] = idm[4];
frameSpace[c++] = idm[5];
frameSpace[c++] = idm[6];
frameSpace[c++] = idm[7];
2019-03-10 03:34:41 +08:00
//number of services
frameSpace[c++] = 0x01;
2019-03-10 03:34:41 +08:00
//service code
frameSpace[c++] = (SERVICE_FELICA_LITE_READONLY >> 8);
frameSpace[c++] = SERVICE_FELICA_LITE_READONLY & 0xFF;
2019-03-10 03:34:41 +08:00
//number of blocks
frameSpace[c++] = blocknum;
2019-03-10 07:00:59 +08:00
for (i = 0; i < blocknum; i++) {
2017-11-11 08:26:53 +08:00
2019-03-10 03:34:41 +08:00
//3-byte block
if (blocks[i] >= 256) {
2019-03-10 07:00:59 +08:00
frameSpace[c++] = 0x00;
frameSpace[c++] = (blocks[i] >> 8); //block number, little endian....
frameSpace[c++] = (blocks[i] & 0xff);
2017-11-11 08:26:53 +08:00
} else {
frameSpace[c++] = 0x80;
frameSpace[c++] = blocks[i];
2017-10-21 02:30:17 +08:00
}
}
2019-03-10 03:34:41 +08:00
//set length
2019-03-10 07:00:59 +08:00
frameSpace[2] = c - 2;
2020-01-22 01:07:18 +08:00
//Add CRC
AddCrc(frameSpace + 2, c - 2);
2017-10-21 02:30:17 +08:00
}
2023-08-09 14:55:20 +08:00
static void TransmitFor18092_AsReader(const uint8_t *frame, uint16_t len, const uint32_t *NYI_timing_NYI, uint8_t power, uint8_t highspeed) {
if (NYI_timing_NYI != NULL) {
Dbprintf("Error: TransmitFor18092_AsReader does not check or set parameter NYI_timing_NYI");
return;
}
2020-07-06 21:16:00 +08:00
uint16_t flags = FPGA_MAJOR_MODE_HF_ISO18092;
2019-03-10 07:00:59 +08:00
if (power)
2019-03-10 03:34:41 +08:00
flags |= FPGA_HF_ISO18092_FLAG_READER;
if (highspeed)
flags |= FPGA_HF_ISO18092_FLAG_424K;
2017-10-21 02:30:17 +08:00
2019-03-10 03:34:41 +08:00
FpgaWriteConfWord(flags);
2019-03-10 03:34:41 +08:00
uint32_t curr_transfer_time = ((MAX(felica_nexttransfertime, GetCountSspClk()) & 0xfffffff8) + 8);
2019-03-10 03:34:41 +08:00
while (GetCountSspClk() < curr_transfer_time) {};
2019-03-10 03:34:41 +08:00
felica_lasttime_prox2air_start = curr_transfer_time;
// preamble
2019-03-10 03:34:41 +08:00
// sending 0x00 0x00 0x00 0x00 0x00 0x00
uint16_t c = 0;
while (c < 6) {
// keep tx buffer in a defined state anyway.
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
AT91C_BASE_SSC->SSC_THR = 0x00;
c++;
}
}
// sending data with sync bytes
2019-03-10 03:34:41 +08:00
c = 0;
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Sending frame:");
Dbhexdump(len, frame, 0);
}
2019-03-10 03:34:41 +08:00
while (c < len) {
// Put byte into tx holding register as soon as it is ready
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) {
AT91C_BASE_SSC->SSC_THR = frame[c++];
}
}
2017-10-21 02:30:17 +08:00
2019-03-10 07:00:59 +08:00
/**/
2019-03-10 03:34:41 +08:00
while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) {};
AT91C_BASE_SSC->SSC_THR = 0x00; //minimum delay
2019-03-10 03:34:41 +08:00
while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) {};
AT91C_BASE_SSC->SSC_THR = 0x00; //spin
2019-03-10 07:00:59 +08:00
/**/
2017-10-21 02:30:17 +08:00
2019-03-10 03:34:41 +08:00
// log
LogTrace(
frame,
len,
2019-03-10 07:00:59 +08:00
(felica_lasttime_prox2air_start << 4) + DELAY_ARM2AIR_AS_READER,
((felica_lasttime_prox2air_start + felica_lasttime_prox2air_start) << 4) + DELAY_ARM2AIR_AS_READER,
2019-03-10 03:34:41 +08:00
NULL,
true
);
2019-03-10 07:00:59 +08:00
felica_nexttransfertime = MAX(felica_nexttransfertime, felica_lasttime_prox2air_start + FELICA_REQUEST_GUARD_TIME);
}
// Wait for tag reply
// stop when button is pressed
// or return TRUE when command is captured
bool WaitForFelicaReply(uint16_t maxbytes) {
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG)
Dbprintf("WaitForFelicaReply Start");
2019-03-10 03:34:41 +08:00
uint32_t c = 0;
// power, no modulation
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD);
2019-03-10 03:34:41 +08:00
FelicaFrameReset();
2019-03-10 03:34:41 +08:00
// clear RXRDY:
uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
2020-01-02 05:13:24 +08:00
(void)b;
2019-03-10 03:34:41 +08:00
uint32_t timeout = iso18092_get_timeout();
2019-03-10 07:00:59 +08:00
for (;;) {
WDT_HIT();
2019-03-10 03:34:41 +08:00
if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) {
b = (uint8_t)(AT91C_BASE_SSC->SSC_RHR);
Process18092Byte(b);
if (FelicaFrame.state == STATE_FULL) {
felica_nexttransfertime = MAX(felica_nexttransfertime,
2019-11-01 22:19:09 +08:00
(GetCountSspClk() & 0xfffffff8) - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / 16 + FELICA_FRAME_DELAY_TIME);
2019-11-01 22:06:48 +08:00
2019-03-10 03:34:41 +08:00
LogTrace(
FelicaFrame.framebytes,
FelicaFrame.len,
2019-03-10 07:00:59 +08:00
((GetCountSspClk() & 0xfffffff8) << 4) - DELAY_AIR2ARM_AS_READER - timeout,
((GetCountSspClk() & 0xfffffff8) << 4) - DELAY_AIR2ARM_AS_READER,
2019-03-10 03:34:41 +08:00
NULL,
false
);
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) Dbprintf("All bytes received! STATE_FULL");
2019-03-10 03:34:41 +08:00
return true;
} else if (c++ > timeout && (FelicaFrame.state == STATE_UNSYNCD || FelicaFrame.state == STATE_TRYING_SYNC)) {
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Error: Timeout! STATE_UNSYNCD");
2019-03-10 03:34:41 +08:00
return false;
}
2019-03-10 03:34:41 +08:00
}
}
}
// Set up FeliCa communication (similar to iso14443a_setup)
// field is setup for "Sending as Reader"
static void iso18092_setup(uint8_t fpga_minor_mode) {
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Start iso18092_setup");
2019-03-10 03:34:41 +08:00
LEDsoff();
2021-08-22 05:43:06 +08:00
#if defined XC3
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
#else
2020-07-09 05:11:29 +08:00
FpgaDownloadAndGo(FPGA_BITSTREAM_HF_FELICA);
2021-08-22 05:43:06 +08:00
#endif
2019-03-10 03:34:41 +08:00
// allocate command receive buffer
2019-03-10 07:00:59 +08:00
BigBuf_free();
BigBuf_Clear_ext(false);
2019-03-10 03:34:41 +08:00
// Initialize Demod and Uart structs
2019-11-01 22:06:48 +08:00
// DemodInit(BigBuf_malloc(MAX_FRAME_SIZE));
2019-03-10 03:34:41 +08:00
FelicaFrameinit(BigBuf_malloc(FELICA_MAX_FRAME_SIZE));
2019-03-10 03:34:41 +08:00
felica_nexttransfertime = 2 * DELAY_ARM2AIR_AS_READER;
iso18092_set_timeout(2120); // 106 * 20ms maximum start-up time of card
2019-03-10 03:34:41 +08:00
init_table(CRC_FELICA);
2019-03-10 03:34:41 +08:00
// connect Demodulated Signal to ADC:
SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
2019-03-10 03:34:41 +08:00
// Set up the synchronous serial port
2020-07-07 19:18:53 +08:00
FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO18092);
2019-03-10 03:34:41 +08:00
// LSB transfer. Remember to set it back to MSB with
AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0);
2019-03-10 03:34:41 +08:00
// Signal field is on with the appropriate LED
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | fpga_minor_mode);
2019-03-10 03:34:41 +08:00
//20.4 ms generate field, start sending polling command afterwars.
SpinDelay(100);
2019-03-10 03:34:41 +08:00
// Start the timer
StartCountSspClk();
2019-03-10 03:34:41 +08:00
LED_D_ON();
}
2020-05-10 22:59:38 +08:00
static void felica_reset_frame_mode(void) {
switch_off();
//Resetting Frame mode (First set in fpgaloader.c)
AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0);
}
//-----------------------------------------------------------------------------
// RAW FeliCa commands. Send out commands and store answers.
//-----------------------------------------------------------------------------
2019-03-10 03:34:41 +08:00
// arg0 FeliCa flags
// arg1 len of commandbytes
// d.asBytes command bytes to send
2023-08-09 14:55:20 +08:00
void felica_sendraw(const PacketCommandNG *c) {
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) Dbprintf("FeliCa_sendraw Enter");
2019-04-18 06:12:52 +08:00
felica_command_t param = c->oldarg[0];
size_t len = c->oldarg[1] & 0xffff;
2023-08-09 14:55:20 +08:00
const uint8_t *cmd = c->data.asBytes;
2019-04-16 21:05:01 +08:00
uint32_t arg0;
2019-03-10 03:34:41 +08:00
felica_card_select_t card;
2019-03-10 03:34:41 +08:00
if ((param & FELICA_CONNECT))
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Clear trace");
2019-10-17 19:48:34 +08:00
clear_trace();
2019-03-10 03:34:41 +08:00
set_tracing(true);
iso18092_setup(FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD);
2019-03-10 03:34:41 +08:00
if ((param & FELICA_CONNECT)) {
// notify client selecting status.
// if failed selecting, turn off antenna and quite.
2019-03-10 07:00:59 +08:00
if (!(param & FELICA_NO_SELECT)) {
2019-03-10 03:34:41 +08:00
arg0 = felica_select_card(&card);
reply_mix(CMD_ACK, arg0, sizeof(card.uid), 0, &card, sizeof(felica_card_select_t));
2019-10-17 19:48:34 +08:00
if (arg0 > 0) {
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Error: Failed selecting card! ");
felica_reset_frame_mode();
return;
}
2019-03-10 03:34:41 +08:00
}
2019-10-17 19:48:34 +08:00
} else {
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) Dbprintf("No card selection");
2019-03-10 03:34:41 +08:00
}
2019-03-10 03:34:41 +08:00
if ((param & FELICA_RAW)) {
2019-03-10 03:34:41 +08:00
// 2 sync, 1 len, 2crc == 5
2019-03-10 07:00:59 +08:00
uint8_t *buf = BigBuf_malloc(len + 5);
2019-03-10 03:34:41 +08:00
// add sync bits
buf[0] = 0xb2;
buf[1] = 0x4d;
buf[2] = len;
2019-03-10 03:34:41 +08:00
// copy command
2019-03-10 07:00:59 +08:00
memcpy(buf + 2, cmd, len);
2019-03-10 03:34:41 +08:00
if ((param & FELICA_APPEND_CRC)) {
// Don't append crc on empty bytearray...
2019-03-10 07:00:59 +08:00
if (len > 0) {
2022-04-27 10:38:28 +08:00
AddCrc(buf + 2, len);
2019-03-10 03:34:41 +08:00
}
}
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Transmit Frame (no CRC shown):");
Dbhexdump(len, buf, 0);
Dbprintf("Buffer Length: %i", buf[2] + 4);
};
2019-03-10 07:00:59 +08:00
TransmitFor18092_AsReader(buf, buf[2] + 4, NULL, 1, 0);
arg0 = WaitForFelicaReply(1024);
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Received Frame Code: %d", arg0);
Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0);
};
2019-10-21 00:28:07 +08:00
uint32_t result = reply_mix(CMD_ACK, FelicaFrame.len, arg0, 0, FelicaFrame.framebytes, FelicaFrame.len);
2019-10-17 19:48:34 +08:00
if (result) {
Dbprintf("Reply to Client Error Code: %i", result);
}
2019-03-10 03:34:41 +08:00
}
2019-10-17 19:48:34 +08:00
if ((param & FELICA_NO_DISCONNECT)) {
Dbprintf("Disconnect");
}
2021-08-22 05:02:27 +08:00
if (g_dbglevel >= DBG_DEBUG)
Dbprintf("FeliCa_sendraw Exit");
felica_reset_frame_mode();
return;
}
void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) {
2021-03-27 04:01:50 +08:00
2019-11-01 22:06:48 +08:00
clear_trace();
set_tracing(true);
2019-03-10 07:00:59 +08:00
iso18092_setup(FPGA_HF_ISO18092_FLAG_NOMOD);
2021-03-27 04:01:50 +08:00
LED_D_ON();
2021-03-27 04:01:50 +08:00
int retval = PM3_SUCCESS;
int remFrames = (samplesToSkip) ? samplesToSkip : 0;
2019-11-01 22:06:48 +08:00
int trigger_cnt = 0;
uint32_t timeout = iso18092_get_timeout();
bool isReaderFrame = true;
2021-03-27 04:01:50 +08:00
uint8_t flip = 0;
uint16_t checker = 0;
for (;;) {
WDT_HIT();
2021-03-27 04:01:50 +08:00
// since simulation is a tight time critical loop,
2021-04-08 16:44:31 +08:00
// we only check for user request to end at iteration 3000, 9000.
2021-03-27 04:01:50 +08:00
if (flip == 3) {
if (data_available()) {
retval = PM3_EOPABORTED;
break;
}
flip = 0;
}
if (checker >= 3000) {
if (BUTTON_PRESS()) {
retval = PM3_EOPABORTED;
break;
}
flip++;
checker = 0;
}
++checker;
if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) {
2021-03-27 04:01:50 +08:00
uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR);
2019-01-09 19:05:29 +08:00
Process18092Byte(dist);
2021-03-27 04:01:50 +08:00
2020-01-04 22:55:50 +08:00
if ((dist >= 178) && (++trigger_cnt > triggersToSkip)) {
2021-03-27 04:01:50 +08:00
Dbprintf("triggers To skip kicked %d", dist);
2019-11-01 22:06:48 +08:00
break;
}
2019-01-07 03:42:51 +08:00
if (FelicaFrame.state == STATE_FULL) {
if ((FelicaFrame.framebytes[3] % 2) == 0) {
isReaderFrame = true; // All Reader Frames are even and all Tag frames are odd
} else {
isReaderFrame = false;
}
remFrames--;
2019-11-01 22:19:09 +08:00
if (remFrames <= 0) {
2021-03-27 04:01:50 +08:00
Dbprintf("Stop Sniffing - samples To skip reached!");
2019-11-01 22:06:48 +08:00
break;
}
LogTrace(FelicaFrame.framebytes,
FelicaFrame.len,
((GetCountSspClk() & 0xfffffff8) << 4) - DELAY_AIR2ARM_AS_READER - timeout,
((GetCountSspClk() & 0xfffffff8) << 4) - DELAY_AIR2ARM_AS_READER,
NULL,
isReaderFrame
2019-11-01 22:19:09 +08:00
);
2019-03-10 03:34:41 +08:00
FelicaFrameReset();
}
}
}
2019-03-10 03:34:41 +08:00
switch_off();
//reset framing
AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0);
2021-03-27 04:01:50 +08:00
Dbprintf("Felica sniffing done, tracelen: %i", BigBuf_get_traceLen());
reply_ng(CMD_HF_FELICA_SNIFF, retval, NULL, 0);
LED_D_OFF();
2017-10-21 02:30:17 +08:00
}
2017-11-11 08:26:53 +08:00
#define R_POLL0_LEN 0x16
#define R_POLL1_LEN 0x18
#define R_READBLK_LEN 0x21
2017-10-21 02:30:17 +08:00
//simulate NFC Tag3 card - for now only poll response works
// second half (4 bytes) of NDEF2 goes into nfcid2_0, first into nfcid2_1
2023-08-09 14:55:20 +08:00
void felica_sim_lite(const uint8_t *uid) {
2021-03-25 16:39:40 +08:00
// prepare our 3 responses...
2019-03-10 07:00:59 +08:00
uint8_t resp_poll0[R_POLL0_LEN] = { 0xb2, 0x4d, 0x12, FELICA_POLL_ACK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0xb3, 0x7f};
uint8_t resp_poll1[R_POLL1_LEN] = { 0xb2, 0x4d, 0x14, FELICA_POLL_ACK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0x88, 0xb4, 0xb3, 0x7f};
uint8_t resp_readblk[R_READBLK_LEN] = { 0xb2, 0x4d, 0x1d, FELICA_RDBLK_ACK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x04, 0x01, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x23, 0xcb, 0x6e};
2021-03-25 16:39:40 +08:00
// NFC tag 3/ ISo technically. Many overlapping standards
DbpString("Felica Lite-S simulation start");
2017-11-11 08:26:53 +08:00
Dbprintf("NDEF2 UID: %02x %02x %02x %02x %02x %02x %02x %02x",
2021-03-25 16:39:40 +08:00
uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7]
2019-03-10 07:00:59 +08:00
);
2021-03-25 16:39:40 +08:00
// fill in blanks
for (uint8_t i = 0; i < 8; i++) {
resp_poll0[i + 4] = uid[i];
resp_poll1[i + 4] = uid[i];
resp_readblk[i + 4] = uid[i];
2017-10-21 02:30:17 +08:00
}
2021-03-25 16:39:40 +08:00
// calculate and set CRC
AddCrc(&resp_poll0[2], resp_poll0[2]);
AddCrc(&resp_poll1[2], resp_poll1[2]);
AddCrc(&resp_readblk[2], resp_readblk[2]);
2017-10-21 02:30:17 +08:00
2019-03-10 07:00:59 +08:00
iso18092_setup(FPGA_HF_ISO18092_FLAG_NOMOD);
2017-10-21 02:30:17 +08:00
2021-03-25 16:39:40 +08:00
int retval = PM3_SUCCESS;
int curlen = 0;
uint8_t *curresp = NULL;
2019-03-10 03:34:41 +08:00
bool listenmode = true;
2021-03-25 16:39:40 +08:00
// uint32_t frtm = GetCountSspClk();
2021-04-08 16:44:31 +08:00
2021-03-25 16:39:40 +08:00
uint8_t flip = 0;
uint16_t checker = 0;
2019-03-10 07:00:59 +08:00
for (;;) {
2021-03-25 16:39:40 +08:00
WDT_HIT();
// since simulation is a tight time critical loop,
2021-04-08 16:44:31 +08:00
// we only check for user request to end at iteration 3000, 9000.
2021-03-25 16:39:40 +08:00
if (flip == 3) {
if (data_available()) {
retval = PM3_EOPABORTED;
break;
}
flip = 0;
}
if (checker >= 3000) {
if (BUTTON_PRESS()) {
retval = PM3_EOPABORTED;
break;
}
flip++;
checker = 0;
}
++checker;
2017-10-21 02:30:17 +08:00
WDT_HIT();
if (listenmode) {
2021-03-25 16:39:40 +08:00
// waiting for request...
2017-10-21 02:30:17 +08:00
if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) {
2019-03-10 03:34:41 +08:00
uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR);
2021-03-25 16:39:40 +08:00
// frtm = GetCountSspClk();
2019-03-10 03:34:41 +08:00
Process18092Byte(dist);
2019-01-07 03:42:51 +08:00
if (FelicaFrame.state == STATE_FULL) {
2017-10-21 02:30:17 +08:00
2019-01-07 03:42:51 +08:00
if (FelicaFrame.crc_ok) {
2019-01-07 03:42:51 +08:00
if (FelicaFrame.framebytes[2] == 6 && FelicaFrame.framebytes[3] == 0) {
static uint8_t timeslot = 0;
2017-10-21 02:30:17 +08:00
2021-03-25 16:39:40 +08:00
// polling... there are two types of polling we answer to
2019-03-10 03:34:41 +08:00
if (FelicaFrame.framebytes[6] == 0) {
curresp = resp_poll0;
curlen = R_POLL0_LEN;
listenmode = false;
}
if (FelicaFrame.framebytes[6] == 1) {
curresp = resp_poll1;
curlen = R_POLL1_LEN;
listenmode = false;
2019-03-10 03:34:41 +08:00
}
if (timeslot > FelicaFrame.framebytes[7]) {
// framebytes[7] contains the maximum time slot in which we are allowed to respond (#0..#15)
timeslot = 0;
}
// first time slot (#0) starts after 512 * 64 / fc, slot length equals 256 * 64 / fc
felica_nexttransfertime = GetCountSspClk() - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / 16 + (512 + timeslot * 256) * 64 / 16 + 1;
timeslot++; // we should use a random time slot, but responding in incremental slots should do just fine for now
2017-10-21 02:30:17 +08:00
}
2019-01-07 03:42:51 +08:00
if (FelicaFrame.framebytes[2] > 5 && FelicaFrame.framebytes[3] == 0x06) {
2021-03-25 16:39:40 +08:00
// we should rebuild it depending on page size, but...
// Let's see first
2017-10-21 02:30:17 +08:00
curresp = resp_readblk;
curlen = R_READBLK_LEN;
listenmode = false;
}
2021-03-25 16:39:40 +08:00
// clear frame
2019-01-07 03:42:51 +08:00
FelicaFrameReset();
2017-10-21 02:30:17 +08:00
} else {
2021-03-25 16:39:40 +08:00
// frame invalid, clear it out to allow for the next one
2019-03-10 03:34:41 +08:00
FelicaFrameReset();
}
}
}
}
2021-03-25 16:39:40 +08:00
if (listenmode == false) {
// trying to answer... here to start answering immediately.
// this one is a bit finicky. Seems that being a bit late is better than earlier
// TransmitFor18092_AsReader(curresp, curlen, frtm+512, 0, 0);
2019-03-10 03:34:41 +08:00
TransmitFor18092_AsReader(curresp, curlen, NULL, 0, 0);
2021-03-25 16:39:40 +08:00
// switch back
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | FPGA_HF_ISO18092_FLAG_NOMOD);
2019-03-10 03:34:41 +08:00
FelicaFrameReset();
listenmode = true;
curlen = 0;
curresp = NULL;
2017-10-21 02:30:17 +08:00
}
}
switch_off();
2021-03-25 16:39:40 +08:00
// reset framing
AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0);
2021-03-25 16:39:40 +08:00
Dbprintf("FeliCa Lite-S emulator stopped. Trace length: %d ", BigBuf_get_traceLen());
reply_ng(CMD_HF_FELICALITE_SIMULATE, retval, NULL, 0);
2017-10-21 02:30:17 +08:00
}
2019-10-20 04:12:33 +08:00
#define RES_SVC_LEN 11 + 3
2020-05-10 22:59:38 +08:00
void felica_dump_lite_s(void) {
2019-03-10 03:34:41 +08:00
uint8_t ndef[8];
2019-03-10 07:00:59 +08:00
uint8_t poll[10] = { 0xb2, 0x4d, 0x06, FELICA_POLL_REQ, 0xff, 0xff, 0x00, 0x00, 0x09, 0x21};
uint16_t liteblks[28] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x90, 0x91, 0x92, 0xa0};
2019-03-10 03:34:41 +08:00
// setup device.
iso18092_setup(FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD);
2019-03-10 03:34:41 +08:00
uint8_t blknum;
bool isOK = false;
uint16_t cnt = 0, cntfails = 0;
uint8_t *dest = BigBuf_get_addr();
2017-10-21 02:30:17 +08:00
2022-01-07 02:41:45 +08:00
while ((BUTTON_PRESS() == false) && (data_available() == false)) {
2019-03-10 03:34:41 +08:00
WDT_HIT();
// polling?
//TransmitFor18092_AsReader(poll, 10, GetCountSspClk()+512, 1, 0);
TransmitFor18092_AsReader(poll, 10, NULL, 1, 0);
2019-03-10 03:34:41 +08:00
if (WaitForFelicaReply(512) && FelicaFrame.framebytes[3] == FELICA_POLL_ACK) {
// copy 8bytes to ndef.
memcpy(ndef, FelicaFrame.framebytes + 4, 8);
// for (c=0; c < 8; c++)
2019-03-10 07:00:59 +08:00
// ndef[c] = FelicaFrame.framebytes[c+4];
2019-08-01 06:14:09 +08:00
for (blknum = 0; blknum < ARRAYLEN(liteblks);) {
2019-03-10 03:34:41 +08:00
// block to read.
BuildFliteRdblk(ndef, 1, &liteblks[blknum]);
2019-03-10 03:34:41 +08:00
//TransmitFor18092_AsReader(frameSpace, frameSpace[2]+4, GetCountSspClk()+512, 1, 0);
2020-01-22 01:07:18 +08:00
TransmitFor18092_AsReader(frameSpace, frameSpace[2] + 4, NULL, 1, 0);
2019-03-10 03:34:41 +08:00
// read block
if (WaitForFelicaReply(1024) && FelicaFrame.framebytes[3] == FELICA_RDBLK_ACK) {
2017-10-21 02:30:17 +08:00
2019-03-10 03:34:41 +08:00
dest[cnt++] = liteblks[blknum];
2017-10-21 02:30:17 +08:00
2019-03-10 03:34:41 +08:00
uint8_t *fb = FelicaFrame.framebytes;
dest[cnt++] = fb[12];
dest[cnt++] = fb[13];
2017-10-21 02:30:17 +08:00
2019-03-10 03:34:41 +08:00
//memcpy(dest+cnt, FelicaFrame.framebytes + 15, 16);
//cnt += 16;
2019-03-10 07:00:59 +08:00
for (uint8_t j = 0; j < 16; j++)
dest[cnt++] = fb[15 + j];
2019-03-10 03:34:41 +08:00
blknum++;
cntfails = 0;
2019-03-10 03:34:41 +08:00
// // print raw log.
// Dbprintf("LEN %u | Dump bytes count %u ", FelicaFrame.len, cnt);
2019-03-10 07:00:59 +08:00
Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes + 15, 0);
2019-03-10 03:34:41 +08:00
} else {
cntfails++;
if (cntfails > 12) {
blknum++;
cntfails = 0;
}
}
}
2020-01-22 01:07:18 +08:00
2019-03-10 03:34:41 +08:00
isOK = true;
break;
}
}
switch_off();
2019-03-10 03:34:41 +08:00
//Resetting Frame mode (First set in fpgaloader.c)
2017-10-21 02:30:17 +08:00
AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0);
2019-03-10 03:34:41 +08:00
//setting tracelen - important! it was set by buffer overflow before
2017-10-21 02:30:17 +08:00
set_tracelen(cnt);
2020-04-08 11:29:22 +08:00
reply_mix(CMD_ACK, isOK, cnt, 0, 0, 0);
}