]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * LEGIC RF simulation code | |
3 | * | |
4 | * (c) 2009 Henryk Plötz <henryk@ploetzli.ch> | |
5 | */ | |
6 | ||
7 | #include <proxmark3.h> | |
8 | ||
9 | #include "apps.h" | |
10 | #include "legicrf.h" | |
11 | ||
12 | static struct legic_frame { | |
13 | int num_bytes; | |
14 | int num_bits; | |
15 | char data[10]; | |
16 | } current_frame; | |
17 | ||
18 | static char queries[][4] = { | |
19 | {7, 0x55}, /* 1010 101 */ | |
20 | }; | |
21 | static char responses[][4] = { | |
22 | {6, 0x3b}, /* 1101 11 */ | |
23 | }; | |
24 | ||
25 | static void frame_send(char *response, int num_bytes, int num_bits) | |
26 | { | |
27 | #if 0 | |
28 | /* Use the SSC to send a response. 8-bit transfers, LSBit first, 100us per bit */ | |
29 | #else | |
30 | /* Bitbang the response */ | |
31 | AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; | |
32 | AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; | |
33 | AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; | |
34 | ||
35 | /* Wait for the frame start */ | |
36 | while(AT91C_BASE_TC1->TC_CV < 490) ; | |
37 | ||
38 | int i; | |
39 | for(i=0; i<(num_bytes*8+num_bits); i++) { | |
40 | int nextbit = AT91C_BASE_TC1->TC_CV + 150; | |
41 | int bit = response[i/8] & (1<<(i%8)); | |
42 | if(bit) | |
43 | AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; | |
44 | else | |
45 | AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; | |
46 | while(AT91C_BASE_TC1->TC_CV < nextbit) ; | |
47 | } | |
48 | AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; | |
49 | #endif | |
50 | } | |
51 | ||
52 | static void frame_respond(struct legic_frame *f) | |
53 | { | |
54 | LED_D_ON(); | |
55 | int bitcount = f->num_bytes*8+f->num_bits; | |
56 | int i, r=-1; | |
57 | for(i=0; i<sizeof(queries)/sizeof(queries[0]); i++) { | |
58 | if(bitcount == queries[i][0] && f->data[0] == queries[i][1] && f->data[1] == queries[i][2]) { | |
59 | r = i; | |
60 | break; | |
61 | } | |
62 | } | |
63 | ||
64 | if(r != -1) { | |
65 | frame_send(&responses[r][1], responses[r][0]/8, responses[r][0]%8); | |
66 | LED_A_ON(); | |
67 | } else { | |
68 | LED_A_OFF(); | |
69 | } | |
70 | ||
71 | LED_D_OFF(); | |
72 | } | |
73 | ||
74 | static void frame_append_bit(struct legic_frame *f, int bit) | |
75 | { | |
76 | if(f->num_bytes >= (int)sizeof(f->data)) | |
77 | return; /* Overflow, won't happen */ | |
78 | f->data[f->num_bytes] |= (bit<<f->num_bits); | |
79 | f->num_bits++; | |
80 | if(f->num_bits > 7) { | |
81 | f->num_bits = 0; | |
82 | f->num_bytes++; | |
83 | } | |
84 | } | |
85 | ||
86 | static int frame_is_empty(struct legic_frame *f) | |
87 | { | |
88 | return( (f->num_bytes*8 + f->num_bits) <= 4 ); | |
89 | } | |
90 | ||
91 | static void frame_handle(struct legic_frame *f) | |
92 | { | |
93 | if(f->num_bytes == 0 && f->num_bits == 6) { | |
94 | /* Short path */ | |
95 | return; | |
96 | } | |
97 | if( !frame_is_empty(f) ) { | |
98 | frame_respond(f); | |
99 | } | |
100 | } | |
101 | ||
102 | static void frame_clean(struct legic_frame *f) | |
103 | { | |
104 | if(!frame_is_empty(f)) | |
105 | /* memset(f->data, 0, sizeof(f->data)); */ | |
106 | f->data[0] = f->data[1] = 0; | |
107 | f->num_bits = 0; | |
108 | f->num_bytes = 0; | |
109 | } | |
110 | ||
111 | static void emit(int bit) | |
112 | { | |
113 | if(bit == -1) { | |
114 | frame_handle(¤t_frame); | |
115 | frame_clean(¤t_frame); | |
116 | } else if(bit == 0) { | |
117 | frame_append_bit(¤t_frame, 0); | |
118 | } else if(bit == 1) { | |
119 | frame_append_bit(¤t_frame, 1); | |
120 | } | |
121 | } | |
122 | ||
123 | void LegicRfSimulate(void) | |
124 | { | |
125 | /* ADC path high-frequency peak detector, FPGA in high-frequency simulator mode, | |
126 | * modulation mode set to 212kHz subcarrier. We are getting the incoming raw | |
127 | * envelope waveform on DIN and should send our response on DOUT. | |
128 | * | |
129 | * The LEGIC RF protocol is pulse-pause-encoding from reader to card, so we'll | |
130 | * measure the time between two rising edges on DIN, and no encoding on the | |
131 | * subcarrier from card to reader, so we'll just shift out our verbatim data | |
132 | * on DOUT, 1 bit is 100us. The time from reader to card frame is still unclear, | |
133 | * seems to be 300us-ish. | |
134 | */ | |
135 | SetAdcMuxFor(GPIO_MUXSEL_HIPKD); | |
136 | FpgaSetupSsc(); | |
137 | FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); | |
138 | ||
139 | /* Bitbang the receiver */ | |
140 | AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; | |
141 | AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; | |
142 | ||
143 | /* Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging | |
144 | * this it won't be terribly accurate but should be good enough. | |
145 | */ | |
146 | AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); | |
147 | AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; | |
148 | AT91C_BASE_TC1->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK3; | |
149 | AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; | |
150 | int old_level = 0; | |
151 | ||
152 | /* At TIMER_CLOCK3 (MCK/32) */ | |
153 | #define BIT_TIME_1 150 | |
154 | #define BIT_TIME_0 90 | |
155 | #define BIT_TIME_FUZZ 20 | |
156 | ||
157 | int active = 0; | |
158 | while(!BUTTON_PRESS()) { | |
159 | int level = !!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); | |
160 | int time = AT91C_BASE_TC1->TC_CV; | |
161 | ||
162 | if(level != old_level) { | |
163 | if(level == 1) { | |
164 | AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; | |
165 | if(time > (BIT_TIME_1-BIT_TIME_FUZZ) && time < (BIT_TIME_1+BIT_TIME_FUZZ)) { | |
166 | /* 1 bit */ | |
167 | emit(1); | |
168 | active = 1; | |
169 | LED_B_ON(); | |
170 | } else if(time > (BIT_TIME_0-BIT_TIME_FUZZ) && time < (BIT_TIME_0+BIT_TIME_FUZZ)) { | |
171 | /* 0 bit */ | |
172 | emit(0); | |
173 | active = 1; | |
174 | LED_B_ON(); | |
175 | } else if(active) { | |
176 | /* invalid */ | |
177 | emit(-1); | |
178 | active = 0; | |
179 | LED_B_OFF(); | |
180 | } | |
181 | } | |
182 | } | |
183 | ||
184 | if(time >= (BIT_TIME_1+BIT_TIME_FUZZ) && active) { | |
185 | /* Frame end */ | |
186 | emit(-1); | |
187 | active = 0; | |
188 | LED_B_OFF(); | |
189 | } | |
190 | ||
191 | if(time >= (20*BIT_TIME_1) && (AT91C_BASE_TC1->TC_SR & AT91C_TC_CLKSTA)) { | |
192 | AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; | |
193 | } | |
194 | ||
195 | ||
196 | old_level = level; | |
197 | WDT_HIT(); | |
198 | } | |
199 | } |