From 4b63f940f1cd052084a77b21365caea3570f8b1d Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 20 Oct 2017 20:27:44 +0200 Subject: [PATCH] CHG: FeliCa implemenation by @satsuoni --- armsrc/Makefile | 3 + armsrc/appmain.c | 12 ++ armsrc/apps.h | 5 + armsrc/fpgaloader.h | 10 ++ client/cmdhf.c | 116 ++++++++++++++++- client/cmdhffelica.c | 274 ++++++++++++++++++++++++++++++++++------ client/cmdhffelica.h | 5 + common/protocols.h | 61 +++++++++ fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/fpga_hf.v | 67 ++++++++-- fpga/fpga_lf.bit | Bin 42175 -> 42175 bytes fpga/fpga_lf.v | 2 +- fpga/fpga_nfc.bit | Bin 42176 -> 42176 bytes fpga/fpga_nfc.v | 38 ++++-- fpga/hi_flite.v | 269 +++++++++++++++++++-------------------- fpga/hi_read_rx_xcorr.v | 27 ++-- fpga/hi_read_tx.v | 19 ++- fpga/hi_simulate.v | 9 +- fpga/hi_sniffer.v | 17 ++- include/usb_cmd.h | 3 + 20 files changed, 705 insertions(+), 232 deletions(-) diff --git a/armsrc/Makefile b/armsrc/Makefile index 15e04bbd1..1e40148da 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -21,6 +21,7 @@ APP_CFLAGS = -DWITH_CRC \ -DWITH_ISO14443b \ -DWITH_ISO14443a \ -DWITH_ICLASS \ + -DWITH_FELICA \ -DWITH_HFSNOOP \ -DWITH_HF_YOUNG \ -fno-strict-aliasing -ffunction-sections -fdata-sections @@ -45,6 +46,7 @@ SRC_LF = lfops.c hitag2.c hitagS.c lfsampling.c pcf7931.c lfdemod.c SRC_ISO15693 = iso15693.c iso15693tools.c SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c epa.c SRC_ISO14443b = iso14443b.c +SRC_FELICA = felica.c SRC_CRAPTO1 = crypto1.c des.c aes.c desfire_key.c desfire_crypto.c mifaredesfire.c SRC_CRC = iso14443crc.c crc.c crc16.c crc32.c SRC_EMV = tlv.c emvdataels.c emvutil.c emvcmd.c @@ -93,6 +95,7 @@ ARMSRC = fpgaloader.c \ $(SRC_ICLASS) \ $(SRC_EMV) \ $(SRC_CRC) \ + $(SRC_FELICA) \ parity.c \ usb_cdc.c \ cmd.c \ diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 46a1a3439..8dbf412e3 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -766,6 +766,18 @@ void UsbPacketReceived(uint8_t *packet, int len) { break; #endif +#ifdef WITH_FELICA + case CMD_FELICA_LITE_SIM: + HfSimLite(c->arg[0]); + break; + case CMD_FELICA_SNOOP: + HfSnoopISO18(c->arg[0], c->arg[1]); + break; + case CMD_FELICA_LITE_DUMP: + HfDumpFelicaLiteS(); + break; +#endif + #ifdef WITH_ISO14443a case CMD_SNOOP_ISO_14443a: SniffIso14443a(c->arg[0]); diff --git a/armsrc/apps.h b/armsrc/apps.h index 2661d32c4..d23efa17f 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -233,6 +233,11 @@ bool cmd_send(uint32_t cmd, uint32_t arg0, uint32_t arg1, uint32_t arg2, void* d // util.h void HfSnoop(int , int); +//felica.c +extern void HfSnoopISO18(uint32_t samples, uint32_t triggers); +extern void HfSimLite(uint64_t uid); +extern void HfDumpFelicaLiteS(); + //EMV functions // emvcmd.h extern void EMVTransaction(void); diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 9e1b31cbe..557a136ea 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -85,4 +85,14 @@ extern void switch_off(); #define FPGA_HF_ISO14443A_READER_LISTEN (3<<0) #define FPGA_HF_ISO14443A_READER_MOD (4<<0) + +//options for Felica. +#define FPGA_MAJOR_MODE_ISO18092 (5<<5) +#define FPGA_HF_ISO18092_FLAG_NOMOD (1<<0) //disable modulation module +#define FPGA_HF_ISO18092_FLAG_424K (2<<0) // should enable 414k mode (untested). No autodetect +#define FPGA_HF_ISO18092_FLAG_READER (4<<0) // enables antenna power, to act as a reader instead of tag + + + + #endif \ No newline at end of file diff --git a/client/cmdhf.c b/client/cmdhf.c index 3f59533ae..8051ac463 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -745,6 +745,114 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui return tracepos; } +void printFelica(uint16_t traceLen, uint8_t *trace) +{ + PrintAndLog(" Gap | Src | Data | CRC | Annotation |"); + PrintAndLog("--------|-----|---------------------------------|----------|-------------------|"); + uint16_t tracepos=0; + //I am stripping SYNC + while(tracepos=traceLen) break; + uint16_t gap= (uint16_t)trace[tracepos+1]+((uint16_t)trace[tracepos]>>8); + uint16_t crc_ok=trace[tracepos+2]; + tracepos+=3; + if(tracepos+3>=traceLen) break; + uint16_t len=trace[tracepos+2]; + //printf("!!! %02x %02x %02x %02x %02x %02x %d",trace[tracepos+0],trace[tracepos+1],trace[tracepos+2],trace[tracepos+3],trace[tracepos+4],trace[tracepos+5],len); + tracepos+=3; //skip SYNC + if(tracepos+len+1>=traceLen) break; + uint8_t cmd=trace[tracepos]; + uint8_t isResponse=cmd&1; + char line[32][110]; + for (int j = 0; j < len+1 && j/8 < 32; j++) + { + snprintf(line[j/8]+(( j % 8) * 4), 110, " %02x ", trace[tracepos+j]); + } + char expbuf[50]; + switch(cmd) + { + case FELICA_POLL_REQ:snprintf(expbuf,49,"Poll Req");break; + case FELICA_POLL_ACK :snprintf(expbuf,49,"Poll Resp");break; + + case FELICA_REQSRV_REQ :snprintf(expbuf,49,"Request Srvc Req");break; +case FELICA_REQSRV_ACK :snprintf(expbuf,49,"Request Srv Resp");break; + +case FELICA_RDBLK_REQ :snprintf(expbuf,49,"Read block(s) Req");break; +case FELICA_RDBLK_ACK :snprintf(expbuf,49,"Read block(s) Resp");break; + +case FELICA_WRTBLK_REQ :snprintf(expbuf,49,"Write block(s) Req");break; +case FELICA_WRTBLK_ACK :snprintf(expbuf,49,"Write block(s) Resp");break; +case FELICA_SRCHSYSCODE_REQ :snprintf(expbuf,49,"Search syscode Req");break; +case FELICA_SRCHSYSCODE_ACK :snprintf(expbuf,49,"Search syscode Resp");break; + +case FELICA_REQSYSCODE_REQ :snprintf(expbuf,49,"Request syscode Req");break; +case FELICA_REQSYSCODE_ACK :snprintf(expbuf,49,"Request syscode Resp");break; + +case FELICA_AUTH1_REQ :snprintf(expbuf,49,"Auth1 Req");break; +case FELICA_AUTH1_ACK :snprintf(expbuf,49,"Auth1 Resp");break; + +case FELICA_AUTH2_REQ :snprintf(expbuf,49,"Auth2 Req");break; +case FELICA_AUTH2_ACK :snprintf(expbuf,49,"Auth2 Resp");break; + +case FELICA_RDSEC_REQ :snprintf(expbuf,49,"Secure read Req");break; +case FELICA_RDSEC_ACK :snprintf(expbuf,49,"Secure read Resp");break; + +case FELICA_WRTSEC_REQ :snprintf(expbuf,49,"Secure write Req");break; +case FELICA_WRTSEC_ACK :snprintf(expbuf,49,"Secure write Resp");break; + +case FELICA_REQSRV2_REQ :snprintf(expbuf,49,"Request Srvc v2 Req");break; +case FELICA_REQSRV2_ACK :snprintf(expbuf,49,"Request Srvc v2 Resp");break; + +case FELICA_GETSTATUS_REQ :snprintf(expbuf,49,"Get status Req");break; +case FELICA_GETSTATUS_ACK :snprintf(expbuf,49,"Get status Resp");break; + +case FELICA_OSVER_REQ :snprintf(expbuf,49,"Get OS Version Req");break; +case FELICA_OSVER_ACK :snprintf(expbuf,49,"Get OS Version Resp");break; + +case FELICA_RESET_MODE_REQ :snprintf(expbuf,49,"Reset mode Req");break; +case FELICA_RESET_MODE_ACK :snprintf(expbuf,49,"Reset mode Resp");break; + +case FELICA_AUTH1V2_REQ :snprintf(expbuf,49,"Auth1 v2 Req");break; +case FELICA_AUTH1V2_ACK :snprintf(expbuf,49,"Auth1 v2 Resp");break; + +case FELICA_AUTH2V2_REQ :snprintf(expbuf,49,"Auth2 v2 Req");break; +case FELICA_AUTH2V2_ACK :snprintf(expbuf,49,"Auth2 v2 Resp");break; + +case FELICA_RDSECV2_REQ :snprintf(expbuf,49,"Secure read v2 Req");break; +case FELICA_RDSECV2_ACK :snprintf(expbuf,49,"Secure read v2 Resp");break; +case FELICA_WRTSECV2_REQ :snprintf(expbuf,49,"Secure write v2 Req");break; +case FELICA_WRTSECV2_ACK :snprintf(expbuf,49,"Secure write v2 Resp");break; + +case FELICA_UPDATE_RNDID_REQ :snprintf(expbuf,49,"Update IDr Req");break; +case FELICA_UPDATE_RNDID_ACK :snprintf(expbuf,49,"Update IDr Resp");break; + default: snprintf(expbuf,49,"Unknown");break; + } + + int num_lines = MIN((len )/16 + 1, 16); + for (int j = 0; j < num_lines ; j++) + { + if (j == 0) { + PrintAndLog("%7d | %s |%-32s |%02x %02x %s| %s", + gap, + (isResponse ? "Tag" : "Rdr"), + line[j], + trace[tracepos+len], + trace[tracepos+len+1], + (crc_ok) ? "OK" : "NG", + expbuf); + } else { + PrintAndLog(" | |%-32s | | ", + line[j]); + } + } + tracepos+=len+1; + } + PrintAndLog(""); + +} + + int usage_hf_list(){ PrintAndLog("List protocol data in trace buffer."); PrintAndLog("Usage: hf list [f][c]"); @@ -863,6 +971,12 @@ int CmdHFList(const char *Cmd) { PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); PrintAndLog(""); + if(protocol==FELICA) +{ +printFelica(traceLen,trace); +} +else +{ PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); if ( protocol == ISO_14443A ) PrintAndLog("iso14443a - All times are in carrier periods (1/13.56Mhz)"); @@ -882,7 +996,7 @@ int CmdHFList(const char *Cmd) { while(tracepos < traceLen) { tracepos = printTraceLine(tracepos, traceLen, trace, protocol, showWaitCycles, markCRCBytes); } - +} free(trace); return 0; } diff --git a/client/cmdhffelica.c b/client/cmdhffelica.c index aeebd919b..643a7b744 100644 --- a/client/cmdhffelica.c +++ b/client/cmdhffelica.c @@ -13,23 +13,44 @@ static int CmdHelp(const char *Cmd); int usage_hf_felica_sim(void) { PrintAndLog("\n Emulating ISO/18092 FeliCa tag \n"); - PrintAndLog("usage: hf felica sim [h] t u [v]"); + PrintAndLog("usage: hf felica sim [h] t [v]"); PrintAndLog("options: "); PrintAndLog(" h : This help"); PrintAndLog(" t : 1 = FeliCa"); - PrintAndLog(" : 2 = FeliCaS"); + PrintAndLog(" : 2 = FeliCaLiteS"); PrintAndLog(" v : (Optional) Verbose"); PrintAndLog("samples:"); - PrintAndLog(" hf felica sim t 1 u 11223344556677"); + PrintAndLog(" hf felica sim t 1 "); return 0; } + int usage_hf_felica_sniff(void){ PrintAndLog("It get data from the field and saves it into command buffer."); PrintAndLog("Buffer accessible from command 'hf list felica'"); - PrintAndLog("Usage: hf felica sniff [c][r]"); - PrintAndLog("c - triggered by first data from card"); - PrintAndLog("r - triggered by first 7-bit request from reader (REQ,WUP,...)"); - PrintAndLog("sample: hf felica sniff c r"); + PrintAndLog("Usage: hf felica sniff "); + PrintAndLog(" s samples to skip (decimal)"); + PrintAndLog(" t triggers to skip (decimal)"); + PrintAndLog("samples:"); + PrintAndLog(" hf felica sniff s 1000"); + return 0; +} +int usage_hf_felica_simlite(void) { + PrintAndLog("\n Emulating ISO/18092 FeliCa Lite tag \n"); + PrintAndLog("usage: hf felica litesim [h] u "); + PrintAndLog("options: "); + PrintAndLog(" h : This help"); + PrintAndLog(" uid : UID in hexsymbol"); + PrintAndLog("samples:"); + PrintAndLog(" hf felica litesim 11223344556677"); + return 0; +} +int usage_hf_felica_dumplite(void) { + PrintAndLog("\n Dump ISO/18092 FeliCa Lite tag \n"); + PrintAndLog("usage: hf felica litedump [h]"); + PrintAndLog("options: "); + PrintAndLog(" h : This help"); + PrintAndLog("samples:"); + PrintAndLog(" hf felica litedump"); return 0; } int usage_hf_felica_raw(void){ @@ -66,13 +87,6 @@ int CmdHFFelicaReader(const char *Cmd) { iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); - - /* - 0: couldn't read - 1: OK, with ATS - 2: OK, no ATS - 3: proprietary Anticollision - */ uint64_t select_status = resp.arg[0]; if (select_status == 0) { @@ -81,10 +95,6 @@ int CmdHFFelicaReader(const char *Cmd) { return 0; } - PrintAndLog(" UID : %s", sprint_hex(card.uid, card.uidlen)); - PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); - PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); - return select_status; } @@ -98,8 +108,8 @@ int CmdHFFelicaSim(const char *Cmd) { int uidlen = 0; bool verbose = false; - while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch(param_getchar(Cmd, cmdp)) { + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (param_getchar(Cmd, cmdp)) { case 'h': case 'H': return usage_hf_felica_sim(); @@ -155,22 +165,211 @@ int CmdHFFelicaSim(const char *Cmd) { return 0; } -int CmdHFFelicaSniff(const char *Cmd) { - int param = 0; - uint8_t ctmp; - for (int i = 0; i < 2; i++) { - ctmp = param_getchar(Cmd, i); - if (ctmp == 'h' || ctmp == 'H') return usage_hf_felica_sniff(); - if (ctmp == 'c' || ctmp == 'C') param |= 0x01; - if (ctmp == 'r' || ctmp == 'R') param |= 0x02; - } +int CmdHFFelicaSniff(const char *Cmd) { - UsbCommand c = {CMD_FELICA_SNOOP, {param, 0, 0}}; + uint8_t cmdp = 0; + uint64_t samples2skip = 0; + uint64_t triggers2skip = 0; + bool errors = false; + + while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch(param_getchar(Cmd, cmdp)) { + case 'h': + case 'H': + return usage_hf_felica_sniff(); + case 's': + case 'S': + samples2skip = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 't': + case 'T': + triggers2skip = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + //Validations + if (errors || cmdp == 0) return usage_hf_felica_sniff(); + + UsbCommand c = {CMD_FELICA_SNOOP, {samples2skip, triggers2skip, 0}}; clearCommandBuffer(); SendCommand(&c); return 0; } +// uid hex +int CmdHFFelicaSimLite(const char *Cmd) { + + uint64_t uid = param_get64ex(Cmd, 0, 0, 16); + + if (!uid) + return usage_hf_felica_simlite(); + + UsbCommand c = {CMD_FELICA_LITE_SIM, {uid, 0, 0} }; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} + +uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace,uint16_t tracelen) { + if (tracepos+19 >= tracelen) + return tracelen; + + trace += tracepos; + uint8_t blocknum = trace[0]; + uint8_t status1 = trace[1]; + uint8_t status2 = trace[2]; + + char line[110] = {0}; + for (int j = 0; j < 16; j++) { + snprintf(line+( j * 4),110, "%02x ", trace[j+3]); + } + + PrintAndLog( "Block number %02x, status: %02x %02x",blocknum,status1, status2); + switch (blocknum) { + case 0x00: PrintAndLog( "S_PAD0: %s",line);break; + case 0x01: PrintAndLog( "S_PAD1: %s",line);break; + case 0x02: PrintAndLog( "S_PAD2: %s",line);break; + case 0x03: PrintAndLog( "S_PAD3: %s",line);break; + case 0x04: PrintAndLog( "S_PAD4: %s",line);break; + case 0x05: PrintAndLog( "S_PAD5: %s",line);break; + case 0x06: PrintAndLog( "S_PAD6: %s",line);break; + case 0x07: PrintAndLog( "S_PAD7: %s",line);break; + case 0x08: PrintAndLog( "S_PAD8: %s",line);break; + case 0x09: PrintAndLog( "S_PAD9: %s",line);break; + case 0x0a: PrintAndLog( "S_PAD10: %s",line);break; + case 0x0b: PrintAndLog( "S_PAD11: %s",line);break; + case 0x0c: PrintAndLog( "S_PAD12: %s",line);break; + case 0x0d: PrintAndLog( "S_PAD13: %s",line);break; + case 0x0E: { + uint32_t regA = trace[3] + (trace[4]>>8) + (trace[5]>>16) + (trace[6]>>24); + uint32_t regB = trace[7] + (trace[8]>>8) + (trace[9]>>16) + (trace[10]>>24); + line[0] = 0; + for (int j = 0; j < 8; j++) + snprintf(line+( j * 2),110, "%02x", trace[j+11]); + PrintAndLog( "REG: regA: %d regB: %d regC: %s ", regA, regB, line); + } + break; + case 0x80: PrintAndLog( "Random Challenge, WO: %s ", line); break; + case 0x81: PrintAndLog( "MAC, only set on dual read: %s ", line); break; + case 0x82: { + char idd[20]; + char idm[20]; + for (int j = 0; j < 8; j++) + snprintf(idd+( j * 2),20, "%02x", trace[j+3]); + + for (int j = 0; j < 6; j++) + snprintf(idm+( j * 2),20, "%02x", trace[j+13]); + + PrintAndLog( "ID Block, IDd: 0x%s DFC: 0x%02x%02x Arb: %s ", idd, trace[11], trace [12], idm); + } + break; + case 0x83: { + char idm[20]; + char pmm[20]; + for (int j = 0; j < 8; j++) + snprintf(idm+( j * 2),20, "%02x", trace[j+3]); + + for (int j = 0; j < 8; j++) + snprintf(pmm+( j * 2),20, "%02x", trace[j+11]); + + PrintAndLog( "DeviceId: IDm: 0x%s PMm: 0x%s ", idm, pmm); + } + break; + case 0x84: PrintAndLog( "SER_C: 0x%02x%02x ", trace[3], trace[4]); break; + case 0x85: PrintAndLog( "SYS_Cl 0x%02x%02x ", trace[3], trace[4]); break; + case 0x86: PrintAndLog( "CKV (key version): 0x%02x%02x ", trace[3], trace[4]); break; + case 0x87: PrintAndLog( "CK (card key), WO: %s ", line); break; + case 0x88: { + PrintAndLog( "Memory Configuration (MC):"); + PrintAndLog( "MAC needed to write state: %s", trace[3+12]? "on" : "off"); + //order might be off here... + PrintAndLog("Write with MAC for S_PAD : %s ", sprint_bin(trace+3+10, 2) ); + PrintAndLog("Write with AUTH for S_PAD : %s ", sprint_bin(trace+3+8, 2) ); + PrintAndLog("Read after AUTH for S_PAD : %s ", sprint_bin(trace+3+6, 2) ); + PrintAndLog( "MAC needed to write CK and CKV: %s", trace[3+5] ? "on" : "off"); + PrintAndLog( "RF parameter: %02x", (trace[3+4] & 0x7) ); + PrintAndLog( "Compatible with NDEF: %s", trace[3+3] ? "yes" : "no"); + PrintAndLog( "Memory config writable : %s", (trace[3+2] == 0xff) ? "yes" : "no"); + PrintAndLog("RW access for S_PAD : %s ", sprint_bin(trace+3, 2) ); + } + break; + case 0x90: { + PrintAndLog( "Write count, RO: %02x %02x %02x ", trace[3], trace[4], trace[5]); + } + break; + case 0x91: { + PrintAndLog( "MAC_A, RW (auth): %s ", line); + } + break; + case 0x92: { + PrintAndLog( "State:"); + PrintAndLog( "Polling disabled: %s", trace[3+8] ? "yes" : "no"); + PrintAndLog( "Authenticated: %s", trace[3] ? "yes" : "no"); + } + break; + case 0xa0: { + PrintAndLog( "CRC of all bloacks match : %s", (trace[3+2]==0xff) ? "no" : "yes"); + } + break; + default: + PrintAndLog( "INVALID %d: %s", blocknum, line); + break; + } + return tracepos+19; +} + +int CmdHFFelicaDumpLite(const char *Cmd) { + + //usage_hf_felica_dumplite(); + + UsbCommand c = {CMD_FELICA_LITE_DUMP, {0,0,0}}; + clearCommandBuffer(); + SendCommand(&c); + + uint16_t tracepos = 0; + uint8_t *trace; + + trace = malloc(USB_CMD_DATA_SIZE); + if ( trace == NULL ) { + PrintAndLog("Cannot allocate memory for trace"); + return 1; + } + + // Query for the size of the trace + UsbCommand response; + GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0); + if ( !WaitForResponseTimeout(CMD_ACK, &response, 4000) ) { + PrintAndLog("timeout while waiting for reply."); + return 1; + } + + uint16_t traceLen = response.arg[2]; + if (traceLen > USB_CMD_DATA_SIZE) { + uint8_t *p = realloc(trace, traceLen); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + return 2; + } + trace = p; + GetFromBigBuf(trace, traceLen, 0); + WaitForResponse(CMD_ACK, NULL); + } + + PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); + while (tracepos < traceLen) { + tracepos = PrintFliteBlock(tracepos, trace, traceLen); + } + free(trace); + return 0; +} + int CmdHFFelicaCmdRaw(const char *cmd) { UsbCommand c = {CMD_FELICA_COMMAND, {0, 0, 0}}; bool reply = 1; @@ -323,12 +522,15 @@ void waitCmdFelica(uint8_t iSelect) { } static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"list", CmdHFFelicaList, 0, "[Deprecated] List ISO 18092/FeliCa history"}, - {"reader", CmdHFFelicaReader, 0, "Act like an ISO18092/FeliCa reader"}, - {"sim", CmdHFFelicaSim, 0, " -- Simulate ISO 18092/FeliCa tag"}, - {"sniff", CmdHFFelicaSniff, 0, "sniff ISO 18092/Felica traffic"}, - {"raw", CmdHFFelicaCmdRaw, 0, "Send raw hex data to tag"}, + {"help", CmdHelp, 1, "This help"}, + {"list", CmdHFFelicaList, 0, "[Deprecated] List ISO 18092/FeliCa history"}, + {"reader", CmdHFFelicaReader, 0, "Act like an ISO18092/FeliCa reader"}, + {"sim", CmdHFFelicaSim, 0, " -- Simulate ISO 18092/FeliCa tag"}, + {"sniff", CmdHFFelicaSniff, 0, "sniff ISO 18092/Felica traffic"}, + {"raw", CmdHFFelicaCmdRaw, 0, "Send raw hex data to tag"}, + + {"litesim", CmdHFFelicaSimLite, 0, " - only reply to poll request"}, + {"litedump", CmdHFFelicaDumpLite, 0, "Wait for and try dumping FelicaLite"}, {NULL, NULL, 0, NULL} }; diff --git a/client/cmdhffelica.h b/client/cmdhffelica.h index 62786c193..bd0ec30ba 100644 --- a/client/cmdhffelica.h +++ b/client/cmdhffelica.h @@ -38,4 +38,9 @@ extern int usage_hf_felica_sniff(void); extern int usage_hf_fFelica_raw(void); void waitCmdFelica(uint8_t iSelect); + +//temp +extern int CmdHFFelicaSimLite(const char *Cmd); +extern int CmdHFFelicaDumpLite(const char *Cmd); + #endif diff --git a/common/protocols.h b/common/protocols.h index 04c203e87..f58b82d36 100644 --- a/common/protocols.h +++ b/common/protocols.h @@ -436,6 +436,67 @@ uint32_t GetT55xxClockBit(uint32_t clock); #define EM4x05_DISABLE_ALLOWED 1<<23 #define EM4x05_READER_TALK_FIRST 1<<24 + + +#define FLITE_SERVICE_RO 0x000B +#define FLITE_SERVICE_RW 0x0009 + +#define FELICA_POLL_REQ 0x00 +#define FELICA_POLL_ACK 0x01 + +#define FELICA_REQSRV_REQ 0x02 +#define FELICA_REQSRV_ACK 0x03 + +#define FELICA_RDBLK_REQ 0x06 +#define FELICA_RDBLK_ACK 0x07 + +#define FELICA_WRTBLK_REQ 0x08 +#define FELICA_WRTBLK_ACK 0x09 + +#define FELICA_SRCHSYSCODE_REQ 0x0a +#define FELICA_SRCHSYSCODE_ACK 0x0b + +#define FELICA_REQSYSCODE_REQ 0x0c +#define FELICA_REQSYSCODE_ACK 0x0d + +#define FELICA_AUTH1_REQ 0x10 +#define FELICA_AUTH1_ACK 0x11 + +#define FELICA_AUTH2_REQ 0x12 +#define FELICA_AUTH2_ACK 0x13 + +#define FELICA_RDSEC_REQ 0x14 +#define FELICA_RDSEC_ACK 0x15 + +#define FELICA_WRTSEC_REQ 0x16 +#define FELICA_WRTSEC_ACK 0x17 + +#define FELICA_REQSRV2_REQ 0x32 +#define FELICA_REQSRV2_ACK 0x33 + +#define FELICA_GETSTATUS_REQ 0x38 +#define FELICA_GETSTATUS_ACK 0x39 + +#define FELICA_OSVER_REQ 0x3c +#define FELICA_OSVER_ACK 0x3d + +#define FELICA_RESET_MODE_REQ 0x3e +#define FELICA_RESET_MODE_ACK 0x3f + +#define FELICA_AUTH1V2_REQ 0x40 +#define FELICA_AUTH1V2_ACK 0x41 + +#define FELICA_AUTH2V2_REQ 0x42 +#define FELICA_AUTH2V2_ACK 0x43 + +#define FELICA_RDSECV2_REQ 0x44 +#define FELICA_RDSECV2_ACK 0x45 +#define FELICA_WRTSECV2_REQ 0x46 +#define FELICA_WRTSECV2_ACK 0x47 + +#define FELICA_UPDATE_RNDID_REQ 0x4C +#define FELICA_UPDATE_RNDID_ACK 0x4D + // iclass / picopass chip config structures and shared routines typedef struct { uint8_t app_limit; //[8] diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 59b25f924a37e36c9b668cc23740be76f2f440ec..77a9331c05527339a2efbe9a5761656af61e534f 100644 GIT binary patch literal 42175 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 literal 42175 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 Tq8$GnR~_Kr{iSUw|0Vwy*k-J@ diff --git a/fpga/fpga_hf.v b/fpga/fpga_hf.v index 264e1b0c6..d056253db 100644 --- a/fpga/fpga_hf.v +++ b/fpga/fpga_hf.v @@ -18,6 +18,8 @@ `include "hi_simulate.v" `include "hi_iso14443a.v" `include "hi_sniffer.v" +`include "hi_flite.v" +//`include "hf_fmod.v" `include "util.v" module fpga_hf( @@ -73,7 +75,10 @@ wire hi_read_tx_shallow_modulation = conf_word[0]; wire hi_read_rx_xcorr_848 = conf_word[0]; // and whether to drive the coil (reader) or just short it (snooper) wire hi_read_rx_xcorr_snoop = conf_word[1]; +// divide subcarrier frequency by 4 +wire hi_read_rx_xcorr_quarter = conf_word[2]; +wire [1:0] hi_read_tx_speed= conf_word [2:1]; // For the high-frequency simulated tag: what kind of modulation to use. wire [2:0] hi_simulate_mod_type = conf_word[2:0]; @@ -90,8 +95,19 @@ hi_read_tx ht( ht_ssp_frame, ht_ssp_din, ssp_dout, ht_ssp_clk, cross_hi, cross_lo, ht_dbg, - hi_read_tx_shallow_modulation + hi_read_tx_shallow_modulation,hi_read_tx_speed, 1'b1 ); + +/*hi_fmod hmf( + pck0, ck_1356meg, ck_1356megb, + hmf_pwr_lo, hmf_pwr_hi, hmf_pwr_oe1, hmf_pwr_oe2, hmf_pwr_oe3, hmf_pwr_oe4, + adc_d, ht_adc_clk, + hmf_ssp_frame, hmf_ssp_din, ssp_dout, hmf_ssp_clk, + cross_hi, cross_lo, + hmf_dbg, + hi_simulate_mod_type +);*/ + hi_read_rx_xcorr hrxc( pck0, ck_1356meg, ck_1356megb, @@ -100,7 +116,7 @@ hi_read_rx_xcorr hrxc( hrxc_ssp_frame, hrxc_ssp_din, ssp_dout, hrxc_ssp_clk, cross_hi, cross_lo, hrxc_dbg, - hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop + hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop, hi_read_rx_xcorr_quarter ); hi_simulate hs( @@ -133,6 +149,16 @@ hi_sniffer he( hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop, hi_read_rx_xcorr_quarter ); +hi_flite hfl( + pck0, ck_1356meg, ck_1356megb, + hfl_pwr_lo, hfl_pwr_hi, hfl_pwr_oe1, hfl_pwr_oe2, hfl_pwr_oe3, hfl_pwr_oe4, + adc_d, hfl_adc_clk, + hfl_ssp_frame, hfl_ssp_din, ssp_dout, hfl_ssp_clk, + cross_hi, cross_lo, + hfl_dbg, + hi_simulate_mod_type +); + // Major modes: // 000 -- HF reader, transmitting to tag; modulation depth selectable @@ -140,19 +166,34 @@ hi_sniffer he( // 010 -- HF simulated tag // 011 -- HF ISO14443-A // 100 -- HF Snoop +// 101 -- HF demod test +// 110 -- Felica modulation, reusing HF reader // 111 -- everything off -mux8 mux_ssp_clk (major_mode, ssp_clk, ht_ssp_clk, hrxc_ssp_clk, hs_ssp_clk, hisn_ssp_clk, he_ssp_clk, 1'b0, 1'b0, 1'b0); -mux8 mux_ssp_din (major_mode, ssp_din, ht_ssp_din, hrxc_ssp_din, hs_ssp_din, hisn_ssp_din, he_ssp_din, 1'b0, 1'b0, 1'b0); -mux8 mux_ssp_frame (major_mode, ssp_frame, ht_ssp_frame, hrxc_ssp_frame, hs_ssp_frame, hisn_ssp_frame, he_ssp_frame, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_oe1 (major_mode, pwr_oe1, ht_pwr_oe1, hrxc_pwr_oe1, hs_pwr_oe1, hisn_pwr_oe1, he_pwr_oe1, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_oe2 (major_mode, pwr_oe2, ht_pwr_oe2, hrxc_pwr_oe2, hs_pwr_oe2, hisn_pwr_oe2, he_pwr_oe2, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_oe3 (major_mode, pwr_oe3, ht_pwr_oe3, hrxc_pwr_oe3, hs_pwr_oe3, hisn_pwr_oe3, he_pwr_oe3, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_oe4 (major_mode, pwr_oe4, ht_pwr_oe4, hrxc_pwr_oe4, hs_pwr_oe4, hisn_pwr_oe4, he_pwr_oe4, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_lo (major_mode, pwr_lo, ht_pwr_lo, hrxc_pwr_lo, hs_pwr_lo, hisn_pwr_lo, he_pwr_lo, 1'b0, 1'b0, 1'b0); -mux8 mux_pwr_hi (major_mode, pwr_hi, ht_pwr_hi, hrxc_pwr_hi, hs_pwr_hi, hisn_pwr_hi, he_pwr_hi, 1'b0, 1'b0, 1'b0); -mux8 mux_adc_clk (major_mode, adc_clk, ht_adc_clk, hrxc_adc_clk, hs_adc_clk, hisn_adc_clk, he_adc_clk, 1'b0, 1'b0, 1'b0); -mux8 mux_dbg (major_mode, dbg, ht_dbg, hrxc_dbg, hs_dbg, hisn_dbg, he_dbg, 1'b0, 1'b0, 1'b0); +//mux8 mux_ssp_clk (major_mode, ssp_clk, ht_ssp_clk, hrxc_ssp_clk, hs_ssp_clk, hisn_ssp_clk, he_ssp_clk, hfl_ssp_clk, hmf_ssp_clk, 1'b0); +//mux8 mux_ssp_din (major_mode, ssp_din, ht_ssp_din, hrxc_ssp_din, hs_ssp_din, hisn_ssp_din, he_ssp_din, hfl_ssp_din, hmf_ssp_din, 1'b0); +//mux8 mux_ssp_frame (major_mode, ssp_frame, ht_ssp_frame, hrxc_ssp_frame, hs_ssp_frame, hisn_ssp_frame, he_ssp_frame, hfl_ssp_frame, hmf_ssp_frame, 1'b0); +//mux8 mux_pwr_oe1 (major_mode, pwr_oe1, ht_pwr_oe1, hrxc_pwr_oe1, hs_pwr_oe1, hisn_pwr_oe1, he_pwr_oe1, hfl_pwr_oe1, hmf_pwr_oe1, 1'b0); +//mux8 mux_pwr_oe2 (major_mode, pwr_oe2, ht_pwr_oe2, hrxc_pwr_oe2, hs_pwr_oe2, hisn_pwr_oe2, he_pwr_oe2, hfl_pwr_oe2, hmf_pwr_oe2, 1'b0); +//mux8 mux_pwr_oe3 (major_mode, pwr_oe3, ht_pwr_oe3, hrxc_pwr_oe3, hs_pwr_oe3, hisn_pwr_oe3, he_pwr_oe3, hfl_pwr_oe3, hmf_pwr_oe3, 1'b0); +//mux8 mux_pwr_oe4 (major_mode, pwr_oe4, ht_pwr_oe4, hrxc_pwr_oe4, hs_pwr_oe4, hisn_pwr_oe4, he_pwr_oe4, hfl_pwr_oe4, hmf_pwr_oe4, 1'b0); +//mux8 mux_pwr_lo (major_mode, pwr_lo, ht_pwr_lo, hrxc_pwr_lo, hs_pwr_lo, hisn_pwr_lo, he_pwr_lo, hfl_pwr_lo, hmf_pwr_lo, 1'b0); +//mux8 mux_pwr_hi (major_mode, pwr_hi, ht_pwr_hi, hrxc_pwr_hi, hs_pwr_hi, hisn_pwr_hi, he_pwr_hi, hfl_pwr_hi, hmf_pwr_hi, 1'b0); +//mux8 mux_adc_clk (major_mode, adc_clk, ht_adc_clk, hrxc_adc_clk, hs_adc_clk, hisn_adc_clk, he_adc_clk, hfl_adc_clk, hmf_adc_clk, 1'b0); +//mux8 mux_dbg (major_mode, dbg, ht_dbg, hrxc_dbg, hs_dbg, hisn_dbg, he_dbg, hfl_dbg, hmf_dbg, 1'b0); + + +mux8 mux_ssp_clk (major_mode, ssp_clk, ht_ssp_clk, hrxc_ssp_clk, hs_ssp_clk, hisn_ssp_clk, he_ssp_clk, hfl_ssp_clk, 1'b0, 1'b0); +mux8 mux_ssp_din (major_mode, ssp_din, ht_ssp_din, hrxc_ssp_din, hs_ssp_din, hisn_ssp_din, he_ssp_din, hfl_ssp_din, 1'b0, 1'b0); +mux8 mux_ssp_frame (major_mode, ssp_frame, ht_ssp_frame, hrxc_ssp_frame, hs_ssp_frame, hisn_ssp_frame, he_ssp_frame, hfl_ssp_frame, 1'b0, 1'b0); +mux8 mux_pwr_oe1 (major_mode, pwr_oe1, ht_pwr_oe1, hrxc_pwr_oe1, hs_pwr_oe1, hisn_pwr_oe1, he_pwr_oe1, hfl_pwr_oe1, 1'b0, 1'b0); +mux8 mux_pwr_oe2 (major_mode, pwr_oe2, ht_pwr_oe2, hrxc_pwr_oe2, hs_pwr_oe2, hisn_pwr_oe2, he_pwr_oe2, hfl_pwr_oe2, 1'b0, 1'b0); +mux8 mux_pwr_oe3 (major_mode, pwr_oe3, ht_pwr_oe3, hrxc_pwr_oe3, hs_pwr_oe3, hisn_pwr_oe3, he_pwr_oe3, hfl_pwr_oe3, 1'b0, 1'b0); +mux8 mux_pwr_oe4 (major_mode, pwr_oe4, ht_pwr_oe4, hrxc_pwr_oe4, hs_pwr_oe4, hisn_pwr_oe4, he_pwr_oe4, hfl_pwr_oe4, 1'b0, 1'b0); +mux8 mux_pwr_lo (major_mode, pwr_lo, ht_pwr_lo, hrxc_pwr_lo, hs_pwr_lo, hisn_pwr_lo, he_pwr_lo, hfl_pwr_lo, 1'b0, 1'b0); +mux8 mux_pwr_hi (major_mode, pwr_hi, ht_pwr_hi, hrxc_pwr_hi, hs_pwr_hi, hisn_pwr_hi, he_pwr_hi, hfl_pwr_hi, 1'b0, 1'b0); +mux8 mux_adc_clk (major_mode, adc_clk, ht_adc_clk, hrxc_adc_clk, hs_adc_clk, hisn_adc_clk, he_adc_clk, hfl_adc_clk, 1'b0, 1'b0); +mux8 mux_dbg (major_mode, dbg, ht_dbg, hrxc_dbg, hs_dbg, hisn_dbg, he_dbg, hfl_dbg, 1'b0, 1'b0); // In all modes, let the ADC's outputs be enabled. assign adc_noe = 1'b0; diff --git a/fpga/fpga_lf.bit b/fpga/fpga_lf.bit index 43fbee16c4e7af230f1c5dfb2c31feffe47f3d5d..5574a6bcf8c9bafc3ed2bdab611353ab8e70c713 100644 GIT binary patch delta 27 jcmdmgl4<`*rU}-3h9(Rt44g(rRtBb4#zq@G%a#BDezypA delta 27 jcmdmgl4<`*rU}-329^vd44j6hR)%I)#%3Em%a#BDe>4b! diff --git a/fpga/fpga_lf.v b/fpga/fpga_lf.v index 14b3caafe..1b7a11276 100644 --- a/fpga/fpga_lf.v +++ b/fpga/fpga_lf.v @@ -103,7 +103,7 @@ lo_edge_detect le( // Major modes: // 000 -- LF reader (generic) // 001 -- LF edge detect (generic) -// 010 -- LF passthru +// 010 -- LF passthrough mux8 mux_ssp_clk (major_mode, ssp_clk, lr_ssp_clk, le_ssp_clk, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); mux8 mux_ssp_din (major_mode, ssp_din, lr_ssp_din, 1'b0, lp_ssp_din, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0); diff --git a/fpga/fpga_nfc.bit b/fpga/fpga_nfc.bit index 46483c715a3a6f423e57abc086207b3bbe7a952b..a1e55d24d1e14cdce15dd2a901d18e84da4c1a6d 100644 GIT binary patch literal 42176 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 diff --git a/fpga/fpga_nfc.v b/fpga/fpga_nfc.v index dbc608fb5..e2d328b5f 100644 --- a/fpga/fpga_nfc.v +++ b/fpga/fpga_nfc.v @@ -16,6 +16,7 @@ `include "hi_flite.v" `include "util.v" +`include "hi_sniffer.v" module fpga_nfc( input spck, output miso, input mosi, input ncs, @@ -67,11 +68,11 @@ assign major_mode = conf_word[7:5]; // For the high-frequency receive correlator: frequency against which to // correlate. -//wire hi_read_rx_xcorr_848 = conf_word[0]; +wire hi_read_rx_xcorr_848 = conf_word[0]; // and whether to drive the coil (reader) or just short it (snooper) -//wire hi_read_rx_xcorr_snoop = conf_word[1]; +wire hi_read_rx_xcorr_snoop = conf_word[1]; // divide subcarrier frequency by 4 -//wire hi_read_rx_xcorr_quarter = conf_word[2]; +wire hi_read_rx_xcorr_quarter = conf_word[2]; // For the high-frequency simulated tag: what kind of modulation to use. wire [2:0] hi_simulate_mod_type = conf_word[2:0]; @@ -81,6 +82,15 @@ wire [2:0] hi_simulate_mod_type = conf_word[2:0]; // major modes, and use muxes to connect the outputs of the active mode to // the output pins. //----------------------------------------------------------------------------- +hi_sniffer he( + pck0, ck_1356meg, ck_1356megb, + he_pwr_lo, he_pwr_hi, he_pwr_oe1, he_pwr_oe2, he_pwr_oe3, he_pwr_oe4, + adc_d, he_adc_clk, + he_ssp_frame, he_ssp_din, ssp_dout, he_ssp_clk, + cross_hi, cross_lo, + he_dbg, + hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop, hi_read_rx_xcorr_quarter +); hi_flite hfl( @@ -103,17 +113,17 @@ hi_flite hfl( // 101 -- HF NFC demod, just to copy it for now // 111 -- everything off -mux8 mux_ssp_clk (major_mode, ssp_clk, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_ssp_clk, 1'b0, 1'b0); -mux8 mux_ssp_din (major_mode, ssp_din, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_ssp_din, 1'b0, 1'b0); -mux8 mux_ssp_frame (major_mode, ssp_frame, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_ssp_frame, 1'b0, 1'b0); -mux8 mux_pwr_oe1 (major_mode, pwr_oe1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_pwr_oe1, 1'b0, 1'b0); -mux8 mux_pwr_oe2 (major_mode, pwr_oe2, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_pwr_oe2, 1'b0, 1'b0); -mux8 mux_pwr_oe3 (major_mode, pwr_oe3, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_pwr_oe3, 1'b0, 1'b0); -mux8 mux_pwr_oe4 (major_mode, pwr_oe4, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_pwr_oe4, 1'b0, 1'b0); -mux8 mux_pwr_lo (major_mode, pwr_lo, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_pwr_lo, 1'b0, 1'b0); -mux8 mux_pwr_hi (major_mode, pwr_hi, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_pwr_hi, 1'b0, 1'b0); -mux8 mux_adc_clk (major_mode, adc_clk, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_adc_clk, 1'b0, 1'b0); -mux8 mux_dbg (major_mode, dbg, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, hfl_dbg, 1'b0, 1'b0); +mux8 mux_ssp_clk (major_mode, ssp_clk, 1'b0, 1'b0, 1'b0, 1'b0, he_ssp_clk, hfl_ssp_clk, 1'b0, 1'b0); +mux8 mux_ssp_din (major_mode, ssp_din, 1'b0, 1'b0, 1'b0, 1'b0, he_ssp_din, hfl_ssp_din, 1'b0, 1'b0); +mux8 mux_ssp_frame (major_mode, ssp_frame, 1'b0, 1'b0, 1'b0, 1'b0, he_ssp_frame, hfl_ssp_frame, 1'b0, 1'b0); +mux8 mux_pwr_oe1 (major_mode, pwr_oe1, 1'b0, 1'b0, 1'b0, 1'b0, he_pwr_oe1, hfl_pwr_oe1, 1'b0, 1'b0); +mux8 mux_pwr_oe2 (major_mode, pwr_oe2, 1'b0, 1'b0, 1'b0, 1'b0, he_pwr_oe2, hfl_pwr_oe2, 1'b0, 1'b0); +mux8 mux_pwr_oe3 (major_mode, pwr_oe3, 1'b0, 1'b0, 1'b0, 1'b0, he_pwr_oe3, hfl_pwr_oe3, 1'b0, 1'b0); +mux8 mux_pwr_oe4 (major_mode, pwr_oe4, 1'b0, 1'b0, 1'b0, 1'b0, he_pwr_oe4, hfl_pwr_oe4, 1'b0, 1'b0); +mux8 mux_pwr_lo (major_mode, pwr_lo, 1'b0, 1'b0, 1'b0, 1'b0, he_pwr_lo, hfl_pwr_lo, 1'b0, 1'b0); +mux8 mux_pwr_hi (major_mode, pwr_hi, 1'b0, 1'b0, 1'b0, 1'b0, he_pwr_hi, hfl_pwr_hi, 1'b0, 1'b0); +mux8 mux_adc_clk (major_mode, adc_clk, 1'b0, 1'b0, 1'b0, 1'b0, he_adc_clk, hfl_adc_clk, 1'b0, 1'b0); +mux8 mux_dbg (major_mode, dbg, 1'b0, 1'b0, 1'b0, 1'b0, hfl_dbg, hfl_dbg, 1'b0, 1'b0); // In all modes, let the ADC's outputs be enabled. assign adc_noe = 1'b0; diff --git a/fpga/hi_flite.v b/fpga/hi_flite.v index c7b9d4648..8c8e26d39 100644 --- a/fpga/hi_flite.v +++ b/fpga/hi_flite.v @@ -1,15 +1,11 @@ -// Satsuoni, October 2017, Added FeliCa support -// + //this code demodulates and modulates signal as described in ISO/IEC 18092. That includes packets used for Felica, NFC Tag 3, etc. (which do overlap) //simple envelope following algorithm is used (modification of fail0verflow LF one) is used to combat some nasty aliasing effect with testing phone (envelope looked like sine wave) -// only 212 kbps (fc/64) for now 414 is relatively straightforward... +// only 212 kbps (fc/64) for now 414 is relatively straightforward... though for reader, the selection has to come from ARM // modulation waits for - //market sprocket -doesn't really mean anything ;) -`define SNIFFER 3'b000 -`define TAGSIM_LISTEN 3'b001 //same as SNIFFER, really. demod does not distinguish tag from reader -`define TAGSIM_MODULATE 3'b010 -`define TAGSIM_MOD_NODELAY 3'b011 //not implemented yet. for use with commands other than polling, which might require different timing, as per Felica standard +//redefining mod_type: bits 210: bit 2 - reader drive/power on/off, bit 1 - speed bit, 0:212, 1 -424 bit 0: listen or modulate + module hi_flite( pck0, ck_1356meg, ck_1356megb, @@ -18,7 +14,8 @@ module hi_flite( ssp_frame, ssp_din, ssp_dout, ssp_clk, cross_hi, cross_lo, dbg, - mod_type // maybe used + mod_type // used + ); input pck0, ck_1356meg, ck_1356megb; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; @@ -28,15 +25,18 @@ module hi_flite( output ssp_frame, ssp_din, ssp_clk; input cross_hi, cross_lo; output dbg; - input [2:0] mod_type; // maybe used. + input [2:0] mod_type; // used. assign dbg=0; -// Most off, oe4 for modulation; No reader emulation (would presumably just require switching power on, but I am not sure) -assign pwr_hi = 1'b0; +wire power= mod_type[2]; +wire speed= mod_type[1]; +wire disabl= mod_type[0]; + +// Most off, oe4 for modulation; +// Trying reader emulation (would presumably just require switching power on, but I am not sure) + //;// 1'b0; assign pwr_lo = 1'b0; -assign pwr_oe1 = 1'b0; -assign pwr_oe2 = 1'b0; -assign pwr_oe3 = 1'b0; + //512x64/fc -wait before ts0, 32768 ticks @@ -51,7 +51,7 @@ assign adc_clk = ck_1356meg; `define ithrmin 91//-13'd8 `define ithrmax 160// 13'd8 - +`define min_bitdelay_212 8 //minimum values and corresponding thresholds reg [8:0] curmin=`imin; @@ -76,9 +76,13 @@ reg did_sync=0; `define bithalf_212 32 //half-bit length for 212 kbit -`define bitlen_212 64 //full-bit length for 212 kbit `define bitmlen_212 63 //bit transition edge -`define bitmhalf_212 31 //mod flip + +`define bithalf_424 16 //half-bit length for 212 kbit +`define bitmlen_424 31 //bit transition edge + +wire [7:0]bithalf= speed ? `bithalf_424 : `bithalf_212; +wire [7:0]bitmlen= speed ? `bitmlen_424 : `bitmlen_212; //ssp clock and current values @@ -98,57 +102,36 @@ reg [8:0] ssp_cnt=9'd0; always @(posedge adc_clk) ssp_cnt <= (ssp_cnt + 1); - -reg getting_arm_data=1'b0; - - -reg [47:0] delayline=48'd0; //48-bit preamble delay line. Just push the data into it starting from first SYNC (1) bit coming from ARM Made this long to keep all ARM data received during preamble -reg [5:0] delay_read_ptr=6'd0; // this is supposed to count ARM delay in the buffer. -reg preamble=0; // whether we are sending preamble - - +//maybe change it so that ARM sends preamble as well. +//then: ready bits sent to ARM, 8 bits sent from ARM (all ones), then preamble (all zeros, presumably) - which starts modulation always @(negedge adc_clk) begin //count fc/64 - transfer bits to ARM at the rate they are received - if(ssp_cnt[5:0] == 6'b000000) + if( ((~speed) && (ssp_cnt[5:0] == 6'b000000)) || (speed &&(ssp_cnt[4:0] == 5'b00000))) begin ssp_clk <= 1'b1; + // if(mod_type[2]) + // begin + // ssp_din<=outp[0];//after_hysteresis; + + //outp<={1'b0,outp[7:1]}; + // end + // else ssp_din <= curbit; - //sample ssp_dout? - if(mod_type==`TAGSIM_MODULATE||mod_type==`TAGSIM_MOD_NODELAY) - begin - delayline<={delayline[46:0],ssp_dout}; - if ((~getting_arm_data) && ssp_dout) - begin - getting_arm_data <=1'b1; - delay_read_ptr<=delay_read_ptr+1; - end - else - begin - if (getting_arm_data & preamble) - begin - delay_read_ptr<=delay_read_ptr+1; - end - end - end - else - begin - getting_arm_data <=1'b0; - delay_read_ptr<=6'd0; - end + //sample ssp_dout end - if(ssp_cnt[5:0] == 6'b100000) + if( ( (~speed) && (ssp_cnt[5:0] == 6'b100000)) ||(speed && ssp_cnt[4:0] == 5'b10000)) ssp_clk <= 1'b0; //create frame pulses. TBH, I still don't know what they do exactly, but they are crucial for ARM->FPGA transfer. If the frame is in the beginning of the byte, transfer slows to a crawl for some reason // took me a day to figure THAT out. - if(ssp_cnt[8:0] == 9'd31) + if(( (~speed) && (ssp_cnt[8:0] == 9'd31))||(speed && ssp_cnt[7:0] == 8'd15)) begin ssp_frame <= 1'b1; end - if(ssp_cnt[8:0] == 9'b1011111) + if(( (~speed) && (ssp_cnt[8:0] == 9'b1011111))||(speed &&ssp_cnt[7:0] == 8'b101111) ) begin ssp_frame <= 1'b0; end @@ -158,7 +141,7 @@ end //send current bit (detected in SNIFF mode or the one being modulated in MOD mode, 0 otherwise) -reg ssp_din; +reg ssp_din;//= outp[0]; @@ -168,28 +151,74 @@ reg prv =1'b1; reg[7:0] mid=8'd128; //for simple error correction in mod/demod detection, use maximum of modded/demodded in given interval. Maybe 1 bit is extra? but better safe than sorry. -//modulated coil. set to 1 to modulate low, 0 to keep signal high -reg mod_sig_coil=1'b0; // set TAGSIM__MODULATE on ARM if we want to write... (frame would get lost if done mid-frame...) // start sending over 1s on ssp->arm when we start sending preamble reg counting_desync=1'b0; // are we counting bits since last frame? reg sending=1'b0; // are we actively modulating? -reg [11:0] bit_counts=12'd0;///for timeslots... only support ts=0 for now, at 212 speed -512 fullbits from end of frame. One hopes. +reg [11:0] bit_counts=12'd0;///for timeslots... only support ts=0 for now, at 212 speed -512 fullbits from end of frame. One hopes. might remove those? +//reg [2:0]old_mod; + +//always @(mod_type) //when moving from modulate_mode +//begin +//if (mod_type[2]==1&&old_mod[2]==0) +// bit_counts=0; +//old_mod=mod_type; +//end +//we need some way to flush bit_counts triggers on mod_type changes don't compile +reg dlay; always @(negedge adc_clk) //every data ping? begin //envelope follow code... //////////// - if ((mod_type==`SNIFFER )||(mod_type==`TAGSIM_LISTEN)) + + //move the counter to the outside... + // if (adc_d>=curminthres||try_sync) + if(fccount==bitmlen) begin + if((~try_sync)&&(adc_d768) // should be over ts0 now, without ARM interference... stop counting... + begin + bit_counts<=0; + // counting_desync<=0; + end + else + if((power)) + bit_counts<=0; + else + bit_counts<=bit_counts+1; + // end + end + else + begin + if((~try_sync)&&(adc_dcurmaxthres) //rising edge begin case (state) 0: begin - curmax <= adc_d>155? adc_d :155; + curmax <= adc_d>`imax? adc_d :`imax; state <= 2; end 1: begin @@ -214,7 +243,7 @@ begin begin case (state) 0: begin - curmin <=adc_d<96? adc_d :96; + curmin <=adc_d<`imin? adc_d :`imin; state <=1; end 1: begin @@ -224,7 +253,7 @@ begin 2: begin curminthres <= ( (curmin>>1)+(curmin>>2)+(curmin>>4)+(curmax>>3)+(curmax>>4)); curmaxthres <= ( (curmax>>1)+(curmax>>2)+(curmax>>4)+(curmin>>3)+(curmin>>4)); - curmin <=adc_d<96? adc_d :96; + curmin <=adc_d<`imin? adc_d :`imin; state <=1; end default: @@ -235,7 +264,7 @@ begin if (~try_sync ) //begin modulation, lower edge... begin try_sync <=1; - counting_desync<=1'b0; + //counting_desync<=1'b0; fccount <= 1; did_sync<=0; curbit<=0; @@ -259,7 +288,7 @@ begin if (tsinceedge>=(128)) begin //we might need to start counting... assuming ARM wants to reply to the frame. - counting_desync<=1'b1; + // counting_desync<=1'b1; bit_counts<=1;// i think? 128 is about 2 bits passed... but 1 also works try_sync<=0; did_sync<=0;//desync @@ -278,32 +307,13 @@ begin end end - //move the counter to the outside... - if (adc_d>=curminthres||try_sync) - if(fccount==`bitmlen_212) - begin - fccount<=0; - if (counting_desync) - begin - - if(bit_counts>768) // should be over ts0 now, without ARM interference... stop counting... - begin - bit_counts<=0; - counting_desync<=0; - end - else - bit_counts<=bit_counts+1; - end - end - else - begin - fccount<=fccount+1; - end + + if (try_sync && tsinceedge<128) begin //detect bits in their middle ssp sampling is in sync, so it would sample all bits in order - if (fccount==`bithalf_212) + if (fccount==bithalf) begin if ((~did_sync) && ((prv==1&&(mid>128))||(prv==0&&(mid<=128)))) begin @@ -336,7 +346,7 @@ begin end else begin - if (fccount==`bitmlen_212) + if (fccount==bitmlen) begin // fccount <=0; prv <=(mid>128)?1:0; @@ -367,61 +377,40 @@ begin begin end sending <=0; - - end //listen mode end - else - begin //sim mode start - //not sure how precise do the time slots have to be... is anything within Ts ok? - //keep counting until 576, just in case - if(fccount==`bitmlen_212) - begin - if (bit_counts==512) // - curbit<=1; - else - begin - if(bit_counts>512) - curbit<=mod_sig_coil;//delayline[delay_read_ptr];//bit_counts[0]; - else - curbit<=0; - end - - fccount<=0; - if (bit_counts<=576) //we don't need to count after that... - begin - bit_counts<=bit_counts+1; - if (bit_counts== 512) //should start sending from next tick... i think? - begin - sending <=1; - mod_sig_coil <=1;//modulate... down? - preamble<=1; - end - else - if (bit_counts== 559) - begin - preamble<=0; - end - end - if (sending) - begin //need next bit - if(preamble) - mod_sig_coil<=1; - else - mod_sig_coil<=~delayline[delay_read_ptr]; - end - end - else - begin - fccount<=fccount+1; - - if ((fccount==`bitmhalf_212)&&(sending)) //flip modulation mid-bit - begin - mod_sig_coil<=~mod_sig_coil;//flip - end - end - end //sim mode end - end +//put modulation here to maintain the correct clock. Seems that some readers are sensitive to that +reg pwr_hi; +reg pwr_oe1; +reg pwr_oe2; +reg pwr_oe3; +reg pwr_oe4; -assign pwr_oe4 = mod_sig_coil & (mod_type == `TAGSIM_MODULATE)&sending; +wire mod=((fccount>=bithalf)^dlay)&(~disabl); + +always @(ck_1356megb or ssp_dout or power or disabl or mod) + begin +if (power) + begin + pwr_hi <= ck_1356megb; + pwr_oe1 <= mod; + pwr_oe2 <= mod; + pwr_oe3 <= mod; + pwr_oe4 <= 1'b0; + end +else + begin + pwr_hi <= 1'b0; + pwr_oe1 <= 1'b0; + pwr_oe2 <= 1'b0; + pwr_oe3 <= 1'b0; + pwr_oe4 <= mod; + end + end +//assign pwr_oe4 = 1'b0;// mod_sig_coil & (modulate_mode)&sending & (~mod_type[2]); +//try shallow mod for reader? +//assign pwr_hi= (mod_type[2]) & ck_1356megb; +//assign pwr_oe1= 1'b0; //mod_sig_coil & (modulate_mode)&sending & (mod_type[2]); +//assign pwr_oe2 = 1'b0;// mod_sig_coil & (modulate_mode)&sending & (mod_type[2]); +//assign pwr_oe3 = 1'b0; //mod_sig_coil & (modulate_mode)&sending & (mod_type[2]); endmodule diff --git a/fpga/hi_read_rx_xcorr.v b/fpga/hi_read_rx_xcorr.v index bbd28b0f5..dc8b01bc5 100644 --- a/fpga/hi_read_rx_xcorr.v +++ b/fpga/hi_read_rx_xcorr.v @@ -10,7 +10,7 @@ module hi_read_rx_xcorr( ssp_frame, ssp_din, ssp_dout, ssp_clk, cross_hi, cross_lo, dbg, - xcorr_is_848, snoop + xcorr_is_848, snoop, xcorr_quarter_freq ); input pck0, ck_1356meg, ck_1356megb; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; @@ -20,7 +20,7 @@ module hi_read_rx_xcorr( output ssp_frame, ssp_din, ssp_clk; input cross_hi, cross_lo; output dbg; - input xcorr_is_848, snoop; + input xcorr_is_848, snoop, xcorr_quarter_freq; // Carrier is steady on through this, unless we're snooping. assign pwr_hi = ck_1356megb & (~snoop); @@ -28,18 +28,20 @@ assign pwr_oe1 = 1'b0; assign pwr_oe3 = 1'b0; assign pwr_oe4 = 1'b0; -// Clock divider -reg [0:0] fc_divider; +reg [2:0] fc_div; always @(negedge ck_1356megb) - fc_divider <= fc_divider + 1; -wire fc_div2 = fc_divider[0]; + fc_div <= fc_div + 1; -reg adc_clk; -always @(ck_1356megb) - if (xcorr_is_848) +(* clock_signal = "yes" *) reg adc_clk; // sample frequency, always 16 * fc +always @(ck_1356megb, xcorr_is_848, xcorr_quarter_freq, fc_div) + if (xcorr_is_848 & ~xcorr_quarter_freq) // fc = 847.5 kHz adc_clk <= ck_1356megb; - else - adc_clk <= fc_div2; + else if (~xcorr_is_848 & ~xcorr_quarter_freq) // fc = 424.25 kHz + adc_clk <= fc_div[0]; + else if (xcorr_is_848 & xcorr_quarter_freq) // fc = 212.125 kHz + adc_clk <= fc_div[1]; + else // fc = 106.0625 kHz + adc_clk <= fc_div[2]; // When we're a reader, we just need to do the BPSK demod; but when we're an // eavesdropper, we also need to pick out the commands sent by the reader, @@ -71,8 +73,7 @@ end // so we need a 6-bit counter. reg [5:0] corr_i_cnt; // And a couple of registers in which to accumulate the correlations. -// we would add at most 32 times adc_d, the result can be held in 13 bits. -// Need one additional bit because it can be negative as well +// we would add/sub at most 32 times adc_d, the signed result can be held in 14 bits. reg signed [13:0] corr_i_accum; reg signed [13:0] corr_q_accum; reg signed [7:0] corr_i_out; diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v index f12e64eb6..23d25deff 100644 --- a/fpga/hi_read_tx.v +++ b/fpga/hi_read_tx.v @@ -12,7 +12,7 @@ module hi_read_tx( ssp_frame, ssp_din, ssp_dout, ssp_clk, cross_hi, cross_lo, dbg, - shallow_modulation + shallow_modulation, speed, power ); input pck0, ck_1356meg, ck_1356megb; output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; @@ -23,6 +23,8 @@ module hi_read_tx( input cross_hi, cross_lo; output dbg; input shallow_modulation; + input [1:0] speed; + input power; // The high-frequency stuff. For now, for testing, just bring out the carrier, // and allow the ARM to modulate it over the SSP. @@ -32,6 +34,8 @@ reg pwr_oe2; reg pwr_oe3; reg pwr_oe4; always @(ck_1356megb or ssp_dout or shallow_modulation) +begin +if (power) begin if(shallow_modulation) begin @@ -50,6 +54,15 @@ begin pwr_oe4 <= 1'b0; end end +else +begin + pwr_hi <= 1'b0; + pwr_oe1 <= 1'b0; + pwr_oe2 <= 1'b0; + pwr_oe3 <= 1'b0; + pwr_oe4 <= ~ssp_dout; +end +end // Then just divide the 13.56 MHz clock down to produce appropriate clocks // for the synchronous serial port. @@ -59,7 +72,7 @@ reg [6:0] hi_div_by_128; always @(posedge ck_1356meg) hi_div_by_128 <= hi_div_by_128 + 1; -assign ssp_clk = hi_div_by_128[6]; +assign ssp_clk = speed[1]? (speed[0]? hi_div_by_128[3]: hi_div_by_128[4]) : (speed[0]? hi_div_by_128[5]: hi_div_by_128[6]); reg [2:0] hi_byte_div; @@ -76,7 +89,7 @@ assign adc_clk = ck_1356meg; reg after_hysteresis; always @(negedge adc_clk) begin - if(& adc_d[7:0]) after_hysteresis <= 1'b1; + if(& adc_d[7:4]) after_hysteresis <= 1'b1; else if(~(| adc_d[7:0])) after_hysteresis <= 1'b0; end diff --git a/fpga/hi_simulate.v b/fpga/hi_simulate.v index 6139cfad2..0768c29de 100644 --- a/fpga/hi_simulate.v +++ b/fpga/hi_simulate.v @@ -53,11 +53,6 @@ end // Divide 13.56 MHz by 32 to produce the SSP_CLK // The register is bigger to allow higher division factors of up to /128 -// FPGA_HF_SIMULATOR_NO_MODULATION (0<<0) // 0000 -// FPGA_HF_SIMULATOR_MODULATE_BPSK (1<<0) // 0001 -// FPGA_HF_SIMULATOR_MODULATE_212K (2<<0) // 0010 -// FPGA_HF_SIMULATOR_MODULATE_424K (4<<0) // 0100 -// FPGA_HF_SIMULATOR_MODULATE_424K_8BIT 0x5 // 0101 reg [10:0] ssp_clk_divider; always @(posedge adc_clk) @@ -91,8 +86,8 @@ end // Divide SSP_CLK by 8 to produce the byte framing signal; the phase of // this is arbitrary, because it's just a bitstream. // One nasty issue, though: I can't make it work with both rx and tx at -// once. The phase wrt ssp_clk must be changed. -// TODO to find out why that is and make a better fix. +// once. The phase wrt ssp_clk must be changed. TODO to find out why +// that is and make a better fix. reg [2:0] ssp_frame_divider_to_arm; always @(posedge ssp_clk) ssp_frame_divider_to_arm <= (ssp_frame_divider_to_arm + 1); diff --git a/fpga/hi_sniffer.v b/fpga/hi_sniffer.v index 3a989ce62..3d39413a3 100644 --- a/fpga/hi_sniffer.v +++ b/fpga/hi_sniffer.v @@ -17,8 +17,11 @@ module hi_sniffer( output dbg; input xcorr_is_848, snoop, xcorr_quarter_freq; // not used. + +// let's try hi-pass + // We are only snooping, all off. -assign pwr_hi = 1'b0; +assign pwr_hi = ck_1356megb & xcorr_quarter_freq;//1'b0; assign pwr_lo = 1'b0; assign pwr_oe1 = 1'b0; assign pwr_oe2 = 1'b0; @@ -29,8 +32,12 @@ reg ssp_frame; reg [7:0] adc_d_out = 8'd0; reg [2:0] ssp_cnt = 3'd0; -assign adc_clk = ck_1356meg; -assign ssp_clk = ~ck_1356meg; + +reg [12:0] avg=13'd0; + +assign adc_clk = ck_1356megb; +assign ssp_clk = ~ck_1356megb; + always @(posedge ssp_clk) begin @@ -41,7 +48,9 @@ begin if(ssp_cnt[2:0] == 3'b000) // set frame length begin - adc_d_out[7:0] <= adc_d; + // adc_d_out[7:0] <= (alias_buf>>>3) +8'd126;//( $signed(adc_d-adc_d_old)>1 | $signed(adc_d_old-adc_d)>1)? alias_buf+adc_d-adc_d_old:alias_buf; //alias_buf[11:3]+8'd126;//adc_d; + // adc_d_out[7:0]<=adc_d; + adc_d_out[7:0] <=adc_d;//-(avg>>3) +8'd126; ssp_frame <= 1'b1; end else diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 31ebaef12..271954413 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -171,6 +171,9 @@ typedef struct{ #define CMD_FELICA_SIMULATE_TAG 0x03A0 #define CMD_FELICA_SNOOP 0x03A1 #define CMD_FELICA_COMMAND 0x03A2 +//temp +#define CMD_FELICA_LITE_DUMP 0x03AA +#define CMD_FELICA_LITE_SIM 0x03AB // For measurements of the antenna tuning #define CMD_MEASURE_ANTENNA_TUNING 0x0400