1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
7 //-----------------------------------------------------------------------------
8 // Low frequency TI commands
9 //-----------------------------------------------------------------------------
15 #include "cmdparser.h" // command_t
16 #include "commonutil.h"
22 #include "cliparser.h"
24 static int CmdHelp(const char *Cmd
);
26 int demodTI(bool verbose
) {
27 (void) verbose
; // unused so far
29 f_s = 2000000; % sampling frequency
30 f_l = 123200; % low FSK tone
31 f_h = 134200; % high FSK tone
33 T_l = 119e-6; % low bit duration
34 T_h = 130e-6; % high bit duration
36 l = 2*pi*ones(1, floor(f_s*T_l))*(f_l/f_s);
37 h = 2*pi*ones(1, floor(f_s*T_h))*(f_h/f_s);
39 l = sign(sin(cumsum(l)));
40 h = sign(sin(cumsum(h)));
44 static const int LowTone
[] = {
45 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
46 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
47 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
48 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
49 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
50 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
51 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
52 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
53 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
54 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
55 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
56 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
57 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
58 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
59 1, 1, 1, 1, 1, 1, 1, 1, -1, -1
62 static const int HighTone
[] = {
63 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
64 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
65 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
66 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
67 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
68 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
69 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
70 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
71 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
72 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
73 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
74 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
75 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
76 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
77 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
78 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1,
79 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1,
80 1, 1, 1, 1, 1, 1, 1, 1
83 save_restoreGB(GRAPH_SAVE
);
85 int lowLen
= ARRAYLEN(LowTone
);
86 int highLen
= ARRAYLEN(HighTone
);
87 int convLen
= (highLen
> lowLen
) ? highLen
: lowLen
;
89 int lowSum
= 0, highSum
= 0;
90 int lowTot
= 0, highTot
= 0;
91 int retval
= PM3_ESOFT
;
93 for (i
= 0; i
< GraphTraceLen
- convLen
; i
++) {
97 for (j
= 0; j
< lowLen
; j
++) {
98 lowSum
+= LowTone
[j
] * GraphBuffer
[i
+ j
];
100 for (j
= 0; j
< highLen
; j
++) {
101 highSum
+= HighTone
[j
] * GraphBuffer
[i
+ j
];
103 lowSum
= abs((100 * lowSum
) / lowLen
);
104 highSum
= abs((100 * highSum
) / highLen
);
105 lowSum
= (lowSum
< 0) ? -lowSum
: lowSum
;
106 highSum
= (highSum
< 0) ? -highSum
: highSum
;
108 GraphBuffer
[i
] = (highSum
<< 16) | lowSum
;
111 for (i
= 0; i
< GraphTraceLen
- convLen
- 16; i
++) {
114 // 16 and 15 are f_s divided by f_l and f_h, rounded
115 for (j
= 0; j
< 16; j
++) {
116 lowTot
+= (GraphBuffer
[i
+ j
] & 0xffff);
118 for (j
= 0; j
< 15; j
++) {
119 highTot
+= (GraphBuffer
[i
+ j
] >> 16);
121 GraphBuffer
[i
] = lowTot
- highTot
;
124 GraphTraceLen
-= (convLen
+ 16);
126 RepaintGraphWindow();
128 // TI tag data format is 16 prebits, 8 start bits, 64 data bits,
129 // 16 crc CCITT bits, 8 stop bits, 15 end bits
131 // the 16 prebits are always low
132 // the 8 start and stop bits of a tag must match
133 // the start/stop prebits of a ro tag are 01111110
134 // the start/stop prebits of a rw tag are 11111110
135 // the 15 end bits of a ro tag are all low
136 // the 15 end bits of a rw tag match bits 15-1 of the data bits
138 // Okay, so now we have unsliced soft decisions;
139 // find bit-sync, and then get some bits.
140 // look for 17 low bits followed by 6 highs (common pattern for ro and rw tags)
141 int max
= 0, maxPos
= 0;
142 for (i
= 0; i
< 6000; i
++) {
144 // searching 17 consecutive lows
145 for (j
= 0; j
< 17 * lowLen
; j
++) {
146 dec
-= GraphBuffer
[i
+ j
];
148 // searching 7 consecutive highs
149 for (; j
< 17 * lowLen
+ 6 * highLen
; j
++) {
150 dec
+= GraphBuffer
[i
+ j
];
158 // place a marker in the buffer to visually aid location
159 // of the start of sync
160 GraphBuffer
[maxPos
] = 800;
161 GraphBuffer
[maxPos
+ 1] = -800;
163 // advance pointer to start of actual data stream (after 16 pre and 8 start bits)
164 maxPos
+= 17 * lowLen
;
165 maxPos
+= 6 * highLen
;
167 // place a marker in the buffer to visually aid location
168 // of the end of sync
169 GraphBuffer
[maxPos
] = 800;
170 GraphBuffer
[maxPos
+ 1] = -800;
172 PrintAndLogEx(DEBUG
, "actual data bits start at sample %d", maxPos
);
173 PrintAndLogEx(DEBUG
, "length %d/%d", highLen
, lowLen
);
175 uint8_t bits
[1 + 64 + 16 + 8 + 16];
176 bits
[sizeof(bits
) - 1] = '\0';
178 uint32_t shift3
= 0x7e000000, shift2
= 0, shift1
= 0, shift0
= 0;
180 for (i
= 0; i
< ARRAYLEN(bits
) - 1; i
++) {
181 int high
= 0, low
= 0;
182 for (j
= 0; j
< lowLen
; j
++) {
183 low
-= GraphBuffer
[maxPos
+ j
];
185 for (j
= 0; j
< highLen
; j
++) {
186 high
+= GraphBuffer
[maxPos
+ j
];
192 // bitstream arrives lsb first so shift right
193 shift3
|= (1u << 31);
199 // 128 bit right shift register
200 shift0
= (shift0
>> 1) | (shift1
<< 31);
201 shift1
= (shift1
>> 1) | (shift2
<< 31);
202 shift2
= (shift2
>> 1) | (shift3
<< 31);
205 // place a marker in the buffer between bits to visually aid location
206 GraphBuffer
[maxPos
] = 800;
207 GraphBuffer
[maxPos
+ 1] = -800;
210 RepaintGraphWindow();
212 PrintAndLogEx(DEBUG
, "TI tag : raw tag bits | %s", bits
);
214 TagType
= (shift3
>> 8) & 0xFF;
215 if (TagType
!= ((shift0
>> 16) & 0xFF)) {
216 PrintAndLogEx(DEBUG
, "TI tag : Error: start and stop bits do not match!");
218 } else if (TagType
== 0x7E) {
219 PrintAndLogEx(INFO
, "Readonly TI tag detected.");
220 retval
= PM3_SUCCESS
;
222 } else if (TagType
== 0xFE) {
223 PrintAndLogEx(INFO
, "Rewriteable TI tag detected.");
225 // put 64 bit data into shift1 and shift0
226 shift0
= (shift0
>> 24) | (shift1
<< 8);
227 shift1
= (shift1
>> 24) | (shift2
<< 8);
229 // align 16 bit crc into lower half of shift2
230 shift2
= ((shift2
>> 24) | (shift3
<< 8)) & 0x0FFFF;
232 // align 16 bit "end bits" or "ident" into lower half of shift3
235 // only 15 bits compare, last bit of ident is not valid
236 if ((shift3
^ shift0
) & 0x7FFF) {
237 PrintAndLogEx(WARNING
, "Warning: Ident mismatch!");
239 // WARNING the order of the bytes in which we calc crc below needs checking
240 // i'm 99% sure the crc algorithm is correct, but it may need to eat the
241 // bytes in reverse or something
244 (shift0
>> 0) & 0xFF,
245 (shift0
>> 8) & 0xFF,
246 (shift0
>> 16) & 0xFF,
247 (shift0
>> 24) & 0xFF,
248 (shift1
>> 0) & 0xFF,
249 (shift1
>> 8) & 0xFF,
250 (shift1
>> 16) & 0xFF,
251 (shift1
>> 24) & 0xFF
253 init_table(CRC_KERMIT
);
254 uint16_t calccrc
= crc16_kermit(raw
, sizeof(raw
));
255 const char *crc_str
= (calccrc
== (shift2
& 0xFFFF)) ? _GREEN_("ok") : _RED_("fail");
256 PrintAndLogEx(INFO
, "Tag data = %08X%08X [%04X] (%s)", shift1
, shift0
, calccrc
, crc_str
);
258 if (calccrc
!= (shift2
& 0xFFFF))
259 PrintAndLogEx(WARNING
, "Warning: CRC mismatch, calculated %04X, got %04X", calccrc
, shift2
& 0xFFFF);
261 retval
= PM3_SUCCESS
;
264 PrintAndLogEx(WARNING
, "Unknown tag type.");
268 if (retval
!= PM3_SUCCESS
)
269 save_restoreGB(GRAPH_RESTORE
);
274 static int CmdTIDemod(const char *Cmd
) {
275 CLIParserContext
*ctx
;
276 CLIParserInit(&ctx
, "lf ti demod",
277 "Try to find TI preamble, if found decode / descramble data",
285 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
287 return demodTI(true);
290 // read a TI tag and return its ID
291 static int CmdTIReader(const char *Cmd
) {
292 CLIParserContext
*ctx
;
293 CLIParserInit(&ctx
, "lf ti reader",
295 "lf ti reader -@ -> continuous reader mode"
300 arg_lit0("@", NULL
, "optional - continuous reader mode"),
303 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
304 bool cm
= arg_get_lit(ctx
, 1);
308 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
312 clearCommandBuffer();
313 SendCommandNG(CMD_LF_TI_READ
, NULL
, 0);
314 } while (cm
&& !kbd_enter_pressed());
319 // write new data to a r/w TI tag
320 static int CmdTIWrite(const char *Cmd
) {
322 CLIParserContext
*ctx
;
323 CLIParserInit(&ctx
, "lf ti write",
324 "write to a r/w TI tag.",
325 "lf ti write --raw 1122334455667788\n"
326 "lf ti write --raw 1122334455667788 --crc 1122\n"
331 arg_str1("r", "raw", "<hex>", "raw hex data. 8 bytes max"),
332 arg_str0(NULL
, "crc", "<hex>", "optional - crc"),
335 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
338 uint8_t raw
[8] = {0};
339 CLIGetHexWithReturn(ctx
, 1, raw
, &raw_len
);
342 uint8_t crc
[2] = {0};
343 CLIGetHexWithReturn(ctx
, 2, crc
, &crc_len
);
352 payload
.high
= bytes_to_num(raw
, 4);
353 payload
.low
= bytes_to_num(raw
+ 4, 4);
354 payload
.crc
= bytes_to_num(crc
, crc_len
);
356 clearCommandBuffer();
357 SendCommandNG(CMD_LF_TI_WRITE
, (uint8_t *)&payload
, sizeof(payload
));
358 PrintAndLogEx(SUCCESS
, "Done");
359 PrintAndLogEx(HINT
, "Hint: try " _YELLOW_("`lf ti reader`") " to verify");
363 static command_t CommandTable
[] = {
364 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
365 {"demod", CmdTIDemod
, AlwaysAvailable
, "Demodulate raw bits for TI LF tag from the GraphBuffer"},
366 {"reader", CmdTIReader
, IfPm3Lf
, "Read and decode a TI 134 kHz tag"},
367 {"write", CmdTIWrite
, IfPm3Lf
, "Write new data to a r/w TI 134 kHz tag"},
368 {NULL
, NULL
, NULL
, NULL
}
371 static int CmdHelp(const char *Cmd
) {
372 (void)Cmd
; // Cmd is not used so far
373 CmdsHelp(CommandTable
);
377 int CmdLFTI(const char *Cmd
) {
378 clearCommandBuffer();
379 return CmdsParse(CommandTable
, Cmd
);