]>
Commit | Line | Data |
---|---|---|
ae2181cd BL |
1 | ## |
2 | ## This file is part of the libsigrokdecode project. | |
3 | ## | |
4 | ## Copyright (C) 2016 Benjamin Larsson <benjamin@southpole.se> | |
5 | ## | |
6 | ## This program is free software; you can redistribute it and/or modify | |
7 | ## it under the terms of the GNU General Public License as published by | |
8 | ## the Free Software Foundation; either version 2 of the License, or | |
9 | ## (at your option) any later version. | |
10 | ## | |
11 | ## This program is distributed in the hope that it will be useful, | |
12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | ## GNU General Public License for more details. | |
15 | ## | |
16 | ## You should have received a copy of the GNU General Public License | |
4539e9ca | 17 | ## along with this program; if not, see <http://www.gnu.org/licenses/>. |
ae2181cd BL |
18 | ## |
19 | ||
20 | import sigrokdecode as srd | |
21 | ||
22 | class SamplerateError(Exception): | |
23 | pass | |
24 | ||
25 | class Decoder(srd.Decoder): | |
d47dd3d9 | 26 | api_version = 3 |
ae2181cd BL |
27 | id = 'em4305' |
28 | name = 'EM4305' | |
29 | longname = 'RFID EM4205/EM4305' | |
30 | desc = 'EM4205/EM4305 100-150kHz RFID protocol.' | |
31 | license = 'gplv2+' | |
32 | inputs = ['logic'] | |
33 | outputs = ['em4305'] | |
d6d8a8a4 | 34 | tags = ['IC', 'RFID'] |
ae2181cd BL |
35 | channels = ( |
36 | {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, | |
37 | ) | |
38 | options = ( | |
39 | {'id': 'coilfreq', 'desc': 'Coil frequency', 'default': 125000}, | |
40 | {'id': 'first_field_stop', 'desc': 'First field stop min', 'default': 40}, | |
41 | {'id': 'w_gap', 'desc': 'Write gap min', 'default': 12}, | |
42 | {'id': 'w_one_max', 'desc': 'Write one max', 'default': 32}, | |
43 | {'id': 'w_zero_on_min', 'desc': 'Write zero on min', 'default': 15}, | |
44 | {'id': 'w_zero_off_max', 'desc': 'Write zero off max', 'default': 27}, | |
45 | {'id': 'em4100_decode', 'desc': 'EM4100 decode', 'default': 'on', | |
46 | 'values': ('on', 'off')}, | |
47 | ) | |
48 | annotations = ( | |
49 | ('bit_value', 'Bit value'), | |
50 | ('first_field_stop', 'First field stop'), | |
51 | ('write_gap', 'Write gap'), | |
52 | ('write_mode_exit', 'Write mode exit'), | |
53 | ('bit', 'Bit'), | |
54 | ('opcode', 'Opcode'), | |
55 | ('lock', 'Lock'), | |
56 | ('data', 'Data'), | |
57 | ('password', 'Password'), | |
58 | ('address', 'Address'), | |
59 | ('bitrate', 'Bitrate'), | |
60 | ) | |
61 | annotation_rows = ( | |
62 | ('bits', 'Bits', (0,)), | |
63 | ('structure', 'Structure', (1, 2, 3, 4)), | |
64 | ('fields', 'Fields', (5, 6, 7, 8, 9)), | |
65 | ('decode', 'Decode', (10,)), | |
66 | ) | |
67 | ||
68 | def __init__(self): | |
10aeb8ea GS |
69 | self.reset() |
70 | ||
71 | def reset(self): | |
ae2181cd | 72 | self.samplerate = None |
ae2181cd BL |
73 | self.last_samplenum = None |
74 | self.state = 'FFS_SEARCH' | |
75 | self.bits_pos = [[0 for col in range(3)] for row in range(70)] | |
76 | self.br_string = ['RF/8', 'RF/16', 'Unused', 'RF/32', 'RF/40', | |
77 | 'Unused', 'Unused', 'RF/64',] | |
78 | self.encoder = ['not used', 'Manchester', 'Bi-phase', 'not used'] | |
79 | self.delayed_on = ['No delay', 'Delayed on - BP/8', 'Delayed on - BP/4', 'No delay'] | |
80 | self.em4100_decode1_partial = 0 | |
81 | self.cmds = ['Invalid', 'Login', 'Write word', 'Invalid', 'Read word', 'Disable', 'Protect', 'Invalid'] | |
82 | ||
83 | def metadata(self, key, value): | |
84 | if key == srd.SRD_CONF_SAMPLERATE: | |
85 | self.samplerate = value | |
86 | self.field_clock = self.samplerate / self.options['coilfreq'] | |
87 | self.wzmax = self.options['w_zero_off_max'] * self.field_clock | |
88 | self.wzmin = self.options['w_zero_on_min'] * self.field_clock | |
89 | self.womax = self.options['w_one_max'] * self.field_clock | |
90 | self.ffs = self.options['first_field_stop'] * self.field_clock | |
91 | self.writegap = self.options['w_gap'] * self.field_clock | |
92 | self.nogap = 300 * self.field_clock | |
93 | ||
94 | def start(self): | |
95 | self.out_ann = self.register(srd.OUTPUT_ANN) | |
96 | ||
97 | def decode_config(self, idx): | |
98 | bitrate = self.get_3_bits(idx+2) | |
99 | self.put(self.bits_pos[idx][1], self.bits_pos[idx+5][2], | |
100 | self.out_ann, [10, ['Data rate: ' + \ | |
101 | self.br_string[bitrate], self.br_string[bitrate]]]) | |
102 | encoding = self.bits_pos[idx+6][0]<<0 | self.bits_pos[idx+7][0]<<1 | |
103 | self.put(self.bits_pos[idx+6][1], self.bits_pos[idx+10][2], | |
104 | self.out_ann, [10, ['Encoder: ' + \ | |
105 | self.encoder[encoding], self.encoder[encoding]]]) | |
106 | self.put(self.bits_pos[idx+11][1], self.bits_pos[idx+12][2], self.out_ann, | |
107 | [10, ['Zero bits', 'ZB']]) | |
108 | delay_on = self.bits_pos[idx+13][0]<<0 | self.bits_pos[idx+14][0]<<1 | |
109 | self.put(self.bits_pos[idx+13][1], self.bits_pos[idx+14][2], | |
110 | self.out_ann, [10, ['Delayed on: ' + \ | |
111 | self.delayed_on[delay_on], self.delayed_on[delay_on]]]) | |
112 | lwr = self.bits_pos[idx+15][0]<<3 | self.bits_pos[idx+16][0]<<2 | \ | |
113 | self.bits_pos[idx+18][0]<<1 | self.bits_pos[idx+19][0]<<0 | |
114 | self.put(self.bits_pos[idx+15][1], self.bits_pos[idx+19][2], | |
115 | self.out_ann, [10, ['Last default read word: %d' % lwr, 'LWR: %d' % lwr, '%d' % lwr]]) | |
116 | self.put(self.bits_pos[idx+20][1], self.bits_pos[idx+20][2], | |
117 | self.out_ann, [10, ['Read login: %d' % self.bits_pos[idx+20][0], '%d' % self.bits_pos[idx+20][0]]]) | |
118 | self.put(self.bits_pos[idx+21][1], self.bits_pos[idx+21][2], self.out_ann, | |
119 | [10, ['Zero bits', 'ZB']]) | |
120 | self.put(self.bits_pos[idx+22][1], self.bits_pos[idx+22][2], | |
121 | self.out_ann, [10, ['Write login: %d' % self.bits_pos[idx+22][0], '%d' % self.bits_pos[idx+22][0]]]) | |
122 | self.put(self.bits_pos[idx+23][1], self.bits_pos[idx+24][2], self.out_ann, | |
123 | [10, ['Zero bits', 'ZB']]) | |
124 | self.put(self.bits_pos[idx+25][1], self.bits_pos[idx+25][2], | |
125 | self.out_ann, [10, ['Disable: %d' % self.bits_pos[idx+25][0], '%d' % self.bits_pos[idx+25][0]]]) | |
126 | self.put(self.bits_pos[idx+27][1], self.bits_pos[idx+27][2], | |
127 | self.out_ann, [10, ['Reader talk first: %d' % self.bits_pos[idx+27][0], 'RTF: %d' % self.bits_pos[idx+27][0]]]) | |
128 | self.put(self.bits_pos[idx+28][1], self.bits_pos[idx+28][2], self.out_ann, | |
129 | [10, ['Zero bits', 'ZB']]) | |
130 | self.put(self.bits_pos[idx+29][1], self.bits_pos[idx+29][2], | |
131 | self.out_ann, [10, ['Pigeon mode: %d' % self.bits_pos[idx+29][0], '%d' % self.bits_pos[idx+29][0]]]) | |
132 | self.put(self.bits_pos[idx+30][1], self.bits_pos[idx+34][2], | |
133 | self.out_ann, [10, ['Reserved', 'Res', 'R']]) | |
134 | ||
135 | def put4bits(self, idx): | |
136 | bits = self.bits_pos[idx][0]<<3 | self.bits_pos[idx+1][0]<<2 | \ | |
137 | self.bits_pos[idx+2][0]<<1 | self.bits_pos[idx+3][0] | |
138 | self.put(self.bits_pos[idx][1], self.bits_pos[idx+3][2], self.out_ann, | |
139 | [10, ['%X' % bits]]) | |
140 | ||
141 | def em4100_decode1(self, idx): | |
142 | self.put(self.bits_pos[idx][1], self.bits_pos[idx+9][2], self.out_ann, | |
143 | [10, ['EM4100 header', 'EM header', 'Header', 'H']]) | |
144 | self.put4bits(idx+10) | |
145 | bits = self.bits_pos[idx+15][0]<<3 | self.bits_pos[idx+16][0]<<2 | \ | |
146 | self.bits_pos[idx+18][0]<<1 | self.bits_pos[idx+19][0]<<0 | |
147 | self.put(self.bits_pos[idx+15][1], self.bits_pos[idx+19][2], self.out_ann, | |
148 | [10, ['%X' % bits]]) | |
149 | self.put4bits(idx+21) | |
150 | self.put4bits(idx+27) | |
151 | self.em4100_decode1_partial = self.bits_pos[idx+32][0]<<3 | \ | |
152 | self.bits_pos[idx+33][0]<<2 | self.bits_pos[idx+34][0]<<1 | |
153 | self.put(self.bits_pos[idx+32][1], self.bits_pos[idx+34][2], | |
154 | self.out_ann, [10, ['Partial nibble']]) | |
155 | ||
156 | def em4100_decode2(self, idx): | |
157 | if self.em4100_decode1_partial != 0: | |
158 | bits = self.em4100_decode1_partial + self.bits_pos[idx][0] | |
159 | self.put(self.bits_pos[idx][1], self.bits_pos[idx][2], | |
160 | self.out_ann, [10, ['%X' % bits]]) | |
161 | self.em4100_decode1_partial = 0 | |
162 | else: | |
163 | self.put(self.bits_pos[idx][1], self.bits_pos[idx][2], | |
164 | self.out_ann, [10, ['Partial nibble']]) | |
165 | ||
166 | self.put4bits(idx+2) | |
167 | bits = self.bits_pos[idx+7][0]<<3 | self.bits_pos[idx+9][0]<<2 | \ | |
168 | self.bits_pos[idx+10][0]<<1 | self.bits_pos[idx+11][0]<<0 | |
169 | self.put(self.bits_pos[idx+7][1], self.bits_pos[idx+11][2], self.out_ann, | |
170 | [10, ['%X' % bits]]) | |
171 | self.put4bits(idx+13) | |
172 | self.put4bits(idx+19) | |
173 | bits = self.bits_pos[idx+24][0]<<3 | self.bits_pos[idx+25][0]<<2 | \ | |
174 | self.bits_pos[idx+27][0]<<1 | self.bits_pos[idx+28][0]<<0 | |
175 | self.put(self.bits_pos[idx+24][1], self.bits_pos[idx+28][2], self.out_ann, | |
176 | [10, ['%X' % bits]]) | |
177 | self.put(self.bits_pos[idx+30][1], self.bits_pos[idx+34][2], | |
178 | self.out_ann, [10, ['EM4100 trailer']]) | |
179 | ||
180 | def get_32_bits(self, idx): | |
181 | return self.get_8_bits(idx+27)<<24 | self.get_8_bits(idx+18)<<16 | \ | |
182 | self.get_8_bits(idx+9)<<8 | self.get_8_bits(idx) | |
183 | ||
184 | def get_8_bits(self, idx): | |
185 | retval = 0 | |
186 | for i in range(0, 8): | |
187 | retval <<= 1 | |
188 | retval |= self.bits_pos[i+idx][0] | |
189 | return retval | |
190 | ||
191 | def get_3_bits(self, idx): | |
192 | return self.bits_pos[idx][0]<<2 | self.bits_pos[idx+1][0]<<1 | \ | |
193 | self.bits_pos[idx+2][0] | |
194 | ||
195 | def get_4_bits(self, idx): | |
196 | return self.bits_pos[idx][0]<<0 | self.bits_pos[idx+1][0]<<1 | \ | |
197 | self.bits_pos[idx+2][0]<<2 | self.bits_pos[idx+3][0]<<3 | |
198 | ||
199 | def print_row_parity(self, idx, length): | |
200 | parity = 0 | |
201 | for i in range(0, length): | |
202 | parity += self.bits_pos[i+idx][0] | |
203 | parity = parity & 0x1 | |
204 | if parity == self.bits_pos[idx+length][0]: | |
205 | self.put(self.bits_pos[idx+length][1], self.bits_pos[idx+length][2], self.out_ann, | |
206 | [5, ['Row parity OK', 'Parity OK', 'OK']]) | |
207 | else: | |
208 | self.put(self.bits_pos[idx+length][1], self.bits_pos[idx+length][2], self.out_ann, | |
209 | [5, ['Row parity failed', 'Parity failed', 'Fail']]) | |
210 | ||
211 | def print_col_parity(self, idx): | |
212 | data_1 = self.get_8_bits(idx) | |
213 | data_2 = self.get_8_bits(idx+9) | |
214 | data_3 = self.get_8_bits(idx+9+9) | |
215 | data_4 = self.get_8_bits(idx+9+9+9) | |
216 | col_par = self.get_8_bits(idx+9+9+9+9) | |
217 | col_par_calc = data_1^data_2^data_3^data_4 | |
218 | ||
219 | if col_par == col_par_calc: | |
220 | self.put(self.bits_pos[idx+9+9+9+9][1], self.bits_pos[idx+9+9+9+9+7][2], self.out_ann, | |
221 | [5, ['Column parity OK', 'Parity OK', 'OK']]) | |
222 | else: | |
223 | self.put(self.bits_pos[idx+9+9+9+9][1], self.bits_pos[idx+9+9+9+9+7][2], self.out_ann, | |
224 | [5, ['Column parity failed', 'Parity failed', 'Fail']]) | |
225 | ||
226 | def print_8bit_data(self, idx): | |
227 | data = self.get_8_bits(idx) | |
228 | self.put(self.bits_pos[idx][1], self.bits_pos[idx+7][2], self.out_ann, | |
229 | [9, ['Data' + ': %X' % data, '%X' % data]]) | |
230 | ||
231 | def put_fields(self): | |
232 | if self.bit_nr == 50: | |
233 | self.put(self.bits_pos[0][1], self.bits_pos[0][2], self.out_ann, | |
234 | [4, ['Logic zero']]) | |
235 | self.put(self.bits_pos[1][1], self.bits_pos[4][2], self.out_ann, | |
236 | [4, ['Command', 'Cmd', 'C']]) | |
237 | self.put(self.bits_pos[5][1], self.bits_pos[49][2], self.out_ann, | |
238 | [4, ['Password', 'Passwd', 'Pass', 'P']]) | |
239 | # Get command. | |
240 | cmd = self.get_3_bits(1) | |
241 | self.put(self.bits_pos[1][1], self.bits_pos[3][2], self.out_ann, | |
242 | [5, [self.cmds[cmd]]]) | |
243 | self.print_row_parity(1, 3) | |
244 | ||
245 | # Print data. | |
246 | self.print_8bit_data(5) | |
247 | self.print_row_parity(5, 8) | |
248 | self.print_8bit_data(14) | |
249 | self.print_row_parity(14, 8) | |
250 | self.print_8bit_data(23) | |
251 | self.print_row_parity(23, 8) | |
252 | self.print_8bit_data(32) | |
253 | self.print_row_parity(32, 8) | |
254 | self.print_col_parity(5) | |
255 | if self.bits_pos[49][0] == 0: | |
256 | self.put(self.bits_pos[49][1], self.bits_pos[49][2], self.out_ann, | |
257 | [5, ['Stop bit', 'Stop', 'SB']]) | |
258 | else: | |
259 | self.put(self.bits_pos[49][1], self.bits_pos[49][2], self.out_ann, | |
260 | [5, ['Stop bit error', 'Error']]) | |
261 | ||
262 | if cmd == 1: | |
263 | password = self.get_32_bits(5) | |
264 | self.put(self.bits_pos[12][1], self.bits_pos[46][2], self.out_ann, | |
265 | [10, ['Login password: %X' % password]]) | |
266 | ||
267 | if self.bit_nr == 57: | |
268 | self.put(self.bits_pos[0][1], self.bits_pos[0][2], self.out_ann, | |
269 | [4, ['Logic zero', 'LZ']]) | |
270 | self.put(self.bits_pos[1][1], self.bits_pos[4][2], self.out_ann, | |
271 | [4, ['Command', 'Cmd', 'C']]) | |
272 | self.put(self.bits_pos[5][1], self.bits_pos[11][2], self.out_ann, | |
273 | [4, ['Address', 'Addr', 'A']]) | |
274 | self.put(self.bits_pos[12][1], self.bits_pos[56][2], self.out_ann, | |
275 | [4, ['Data', 'Da', 'D']]) | |
276 | ||
277 | # Get command. | |
278 | cmd = self.get_3_bits(1) | |
279 | self.put(self.bits_pos[1][1], self.bits_pos[3][2], self.out_ann, | |
280 | [5, [self.cmds[cmd]]]) | |
281 | self.print_row_parity(1, 3) | |
282 | ||
283 | # Get address. | |
284 | addr = self.get_4_bits(5) | |
285 | self.put(self.bits_pos[5][1], self.bits_pos[8][2], self.out_ann, | |
286 | [9, ['Addr' + ': %d' % addr, '%d' % addr]]) | |
287 | self.put(self.bits_pos[9][1], self.bits_pos[10][2], self.out_ann, | |
288 | [5, ['Zero bits', 'ZB']]) | |
289 | self.print_row_parity(5, 6) | |
290 | # Print data. | |
291 | self.print_8bit_data(12) | |
292 | self.print_row_parity(12, 8) | |
293 | self.print_8bit_data(21) | |
294 | self.print_row_parity(21, 8) | |
295 | self.print_8bit_data(30) | |
296 | self.print_row_parity(30, 8) | |
297 | self.print_8bit_data(39) | |
298 | self.print_row_parity(39, 8) | |
299 | self.print_col_parity(12) | |
300 | if self.bits_pos[56][0] == 0: | |
301 | self.put(self.bits_pos[56][1], self.bits_pos[56][2], self.out_ann, | |
302 | [5, ['Stop bit', 'Stop', 'SB']]) | |
303 | else: | |
304 | self.put(self.bits_pos[56][1], self.bits_pos[56][2], self.out_ann, | |
305 | [5, ['Stop bit error', 'Error']]) | |
306 | ||
307 | if addr == 4: | |
308 | self.decode_config(12) | |
309 | ||
310 | if addr == 2: | |
311 | password = self.get_32_bits(12) | |
312 | self.put(self.bits_pos[12][1], self.bits_pos[46][2], self.out_ann, | |
313 | [10, ['Write password: %X' % password]]) | |
314 | ||
315 | # If we are programming EM4100 data we can decode it halfway. | |
316 | if addr == 5 and self.options['em4100_decode'] == 'on': | |
317 | self.em4100_decode1(12) | |
318 | if addr == 6 and self.options['em4100_decode'] == 'on': | |
319 | self.em4100_decode2(12) | |
320 | ||
321 | self.bit_nr = 0 | |
322 | ||
323 | def add_bits_pos(self, bit, ss_bit, es_bit): | |
324 | if self.bit_nr < 70: | |
325 | self.bits_pos[self.bit_nr][0] = bit | |
326 | self.bits_pos[self.bit_nr][1] = ss_bit | |
327 | self.bits_pos[self.bit_nr][2] = es_bit | |
328 | self.bit_nr += 1 | |
329 | ||
d47dd3d9 | 330 | def decode(self): |
ae2181cd BL |
331 | if not self.samplerate: |
332 | raise SamplerateError('Cannot decode without samplerate.') | |
d47dd3d9 GS |
333 | |
334 | # Initialize internal state. | |
335 | self.last_samplenum = self.samplenum | |
336 | self.oldsamplenum = 0 | |
337 | self.old_gap_end = 0 | |
338 | self.gap_detected = 0 | |
339 | self.bit_nr = 0 | |
340 | ||
341 | while True: | |
342 | # Ignore identical samples, only process edges. | |
343 | (pin,) = self.wait({0: 'e'}) | |
344 | ||
345 | pl = self.samplenum - self.oldsamplenum | |
346 | pp = pin | |
347 | samples = self.samplenum - self.last_samplenum | |
348 | ||
349 | if self.state == 'FFS_DETECTED': | |
350 | if pl > self.writegap: | |
351 | self.gap_detected = 1 | |
352 | if (self.last_samplenum - self.old_gap_end) > self.nogap: | |
ae2181cd | 353 | self.gap_detected = 0 |
ae2181cd | 354 | self.state = 'FFS_SEARCH' |
d47dd3d9 GS |
355 | self.put(self.old_gap_end, self.last_samplenum, |
356 | self.out_ann, [3, ['Write mode exit']]) | |
357 | self.put_fields() | |
358 | ||
359 | if self.state == 'FFS_SEARCH': | |
360 | if pl > self.ffs: | |
361 | self.gap_detected = 1 | |
362 | self.put(self.last_samplenum, self.samplenum, | |
363 | self.out_ann, [1, ['First field stop', 'Field stop', 'FFS']]) | |
364 | self.state = 'FFS_DETECTED' | |
365 | ||
366 | if self.gap_detected == 1: | |
367 | self.gap_detected = 0 | |
368 | if (self.last_samplenum - self.old_gap_end) > self.wzmin \ | |
369 | and (self.last_samplenum - self.old_gap_end) < self.wzmax: | |
370 | self.put(self.old_gap_end, self.samplenum, | |
371 | self.out_ann, [0, ['0']]) | |
372 | self.add_bits_pos(0, self.old_gap_end, self.samplenum) | |
373 | if (self.last_samplenum - self.old_gap_end) > self.womax \ | |
374 | and (self.last_samplenum-self.old_gap_end) < self.nogap: | |
375 | # One or more 1 bits | |
376 | one_bits = (int)((self.last_samplenum - self.old_gap_end) / self.womax) | |
377 | for ox in range(0, one_bits): | |
378 | bs = (int)(self.old_gap_end+ox*self.womax) | |
379 | be = (int)(self.old_gap_end+ox*self.womax + self.womax) | |
380 | self.put(bs, be, self.out_ann, [0, ['1']]) | |
381 | self.add_bits_pos(1, bs, be) | |
382 | if (self.samplenum - self.last_samplenum) > self.wzmin \ | |
383 | and (self.samplenum - self.last_samplenum) < self.wzmax: | |
384 | bs = (int)(self.old_gap_end+one_bits*self.womax) | |
385 | self.put(bs, self.samplenum, self.out_ann, [0, ['0']]) | |
386 | self.add_bits_pos(0, bs, self.samplenum) | |
387 | ||
388 | self.old_gap_end = self.samplenum | |
389 | ||
390 | if self.state == 'SKIP': | |
391 | self.state = 'FFS_SEARCH' | |
392 | ||
393 | self.oldsamplenum = self.samplenum | |
394 | self.last_samplenum = self.samplenum |