2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2016 Benjamin Larsson <benjamin@southpole.se>
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.
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.
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
20 import sigrokdecode as srd
22 class SamplerateError(Exception):
25 class Decoder(srd.Decoder):
29 longname = 'RFID EM4205/EM4305'
30 desc = 'EM4205/EM4305 100-150kHz RFID protocol.'
35 {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
38 {'id': 'coilfreq', 'desc': 'Coil frequency', 'default': 125000},
39 {'id': 'first_field_stop', 'desc': 'First field stop min', 'default': 40},
40 {'id': 'w_gap', 'desc': 'Write gap min', 'default': 12},
41 {'id': 'w_one_max', 'desc': 'Write one max', 'default': 32},
42 {'id': 'w_zero_on_min', 'desc': 'Write zero on min', 'default': 15},
43 {'id': 'w_zero_off_max', 'desc': 'Write zero off max', 'default': 27},
44 {'id': 'em4100_decode', 'desc': 'EM4100 decode', 'default': 'on',
45 'values': ('on', 'off')},
48 ('bit_value', 'Bit value'),
49 ('first_field_stop', 'First field stop'),
50 ('write_gap', 'Write gap'),
51 ('write_mode_exit', 'Write mode exit'),
56 ('password', 'Password'),
57 ('address', 'Address'),
58 ('bitrate', 'Bitrate'),
61 ('bits', 'Bits', (0,)),
62 ('structure', 'Structure', (1, 2, 3, 4)),
63 ('fields', 'Fields', (5, 6, 7, 8, 9)),
64 ('decode', 'Decode', (10,)),
68 self.samplerate = None
70 self.last_samplenum = None
71 self.state = 'FFS_SEARCH'
72 self.bits_pos = [[0 for col in range(3)] for row in range(70)]
73 self.br_string = ['RF/8', 'RF/16', 'Unused', 'RF/32', 'RF/40',
74 'Unused', 'Unused', 'RF/64',]
75 self.encoder = ['not used', 'Manchester', 'Bi-phase', 'not used']
76 self.delayed_on = ['No delay', 'Delayed on - BP/8', 'Delayed on - BP/4', 'No delay']
77 self.em4100_decode1_partial = 0
78 self.cmds = ['Invalid', 'Login', 'Write word', 'Invalid', 'Read word', 'Disable', 'Protect', 'Invalid']
80 def metadata(self, key, value):
81 if key == srd.SRD_CONF_SAMPLERATE:
82 self.samplerate = value
83 self.field_clock = self.samplerate / self.options['coilfreq']
84 self.wzmax = self.options['w_zero_off_max'] * self.field_clock
85 self.wzmin = self.options['w_zero_on_min'] * self.field_clock
86 self.womax = self.options['w_one_max'] * self.field_clock
87 self.ffs = self.options['first_field_stop'] * self.field_clock
88 self.writegap = self.options['w_gap'] * self.field_clock
89 self.nogap = 300 * self.field_clock
92 self.out_ann = self.register(srd.OUTPUT_ANN)
94 def decode_config(self, idx):
95 bitrate = self.get_3_bits(idx+2)
96 self.put(self.bits_pos[idx][1], self.bits_pos[idx+5][2],
97 self.out_ann, [10, ['Data rate: ' + \
98 self.br_string[bitrate], self.br_string[bitrate]]])
99 encoding = self.bits_pos[idx+6][0]<<0 | self.bits_pos[idx+7][0]<<1
100 self.put(self.bits_pos[idx+6][1], self.bits_pos[idx+10][2],
101 self.out_ann, [10, ['Encoder: ' + \
102 self.encoder[encoding], self.encoder[encoding]]])
103 self.put(self.bits_pos[idx+11][1], self.bits_pos[idx+12][2], self.out_ann,
104 [10, ['Zero bits', 'ZB']])
105 delay_on = self.bits_pos[idx+13][0]<<0 | self.bits_pos[idx+14][0]<<1
106 self.put(self.bits_pos[idx+13][1], self.bits_pos[idx+14][2],
107 self.out_ann, [10, ['Delayed on: ' + \
108 self.delayed_on[delay_on], self.delayed_on[delay_on]]])
109 lwr = self.bits_pos[idx+15][0]<<3 | self.bits_pos[idx+16][0]<<2 | \
110 self.bits_pos[idx+18][0]<<1 | self.bits_pos[idx+19][0]<<0
111 self.put(self.bits_pos[idx+15][1], self.bits_pos[idx+19][2],
112 self.out_ann, [10, ['Last default read word: %d' % lwr, 'LWR: %d' % lwr, '%d' % lwr]])
113 self.put(self.bits_pos[idx+20][1], self.bits_pos[idx+20][2],
114 self.out_ann, [10, ['Read login: %d' % self.bits_pos[idx+20][0], '%d' % self.bits_pos[idx+20][0]]])
115 self.put(self.bits_pos[idx+21][1], self.bits_pos[idx+21][2], self.out_ann,
116 [10, ['Zero bits', 'ZB']])
117 self.put(self.bits_pos[idx+22][1], self.bits_pos[idx+22][2],
118 self.out_ann, [10, ['Write login: %d' % self.bits_pos[idx+22][0], '%d' % self.bits_pos[idx+22][0]]])
119 self.put(self.bits_pos[idx+23][1], self.bits_pos[idx+24][2], self.out_ann,
120 [10, ['Zero bits', 'ZB']])
121 self.put(self.bits_pos[idx+25][1], self.bits_pos[idx+25][2],
122 self.out_ann, [10, ['Disable: %d' % self.bits_pos[idx+25][0], '%d' % self.bits_pos[idx+25][0]]])
123 self.put(self.bits_pos[idx+27][1], self.bits_pos[idx+27][2],
124 self.out_ann, [10, ['Reader talk first: %d' % self.bits_pos[idx+27][0], 'RTF: %d' % self.bits_pos[idx+27][0]]])
125 self.put(self.bits_pos[idx+28][1], self.bits_pos[idx+28][2], self.out_ann,
126 [10, ['Zero bits', 'ZB']])
127 self.put(self.bits_pos[idx+29][1], self.bits_pos[idx+29][2],
128 self.out_ann, [10, ['Pigeon mode: %d' % self.bits_pos[idx+29][0], '%d' % self.bits_pos[idx+29][0]]])
129 self.put(self.bits_pos[idx+30][1], self.bits_pos[idx+34][2],
130 self.out_ann, [10, ['Reserved', 'Res', 'R']])
132 def put4bits(self, idx):
133 bits = self.bits_pos[idx][0]<<3 | self.bits_pos[idx+1][0]<<2 | \
134 self.bits_pos[idx+2][0]<<1 | self.bits_pos[idx+3][0]
135 self.put(self.bits_pos[idx][1], self.bits_pos[idx+3][2], self.out_ann,
138 def em4100_decode1(self, idx):
139 self.put(self.bits_pos[idx][1], self.bits_pos[idx+9][2], self.out_ann,
140 [10, ['EM4100 header', 'EM header', 'Header', 'H']])
141 self.put4bits(idx+10)
142 bits = self.bits_pos[idx+15][0]<<3 | self.bits_pos[idx+16][0]<<2 | \
143 self.bits_pos[idx+18][0]<<1 | self.bits_pos[idx+19][0]<<0
144 self.put(self.bits_pos[idx+15][1], self.bits_pos[idx+19][2], self.out_ann,
146 self.put4bits(idx+21)
147 self.put4bits(idx+27)
148 self.em4100_decode1_partial = self.bits_pos[idx+32][0]<<3 | \
149 self.bits_pos[idx+33][0]<<2 | self.bits_pos[idx+34][0]<<1
150 self.put(self.bits_pos[idx+32][1], self.bits_pos[idx+34][2],
151 self.out_ann, [10, ['Partial nibble']])
153 def em4100_decode2(self, idx):
154 if self.em4100_decode1_partial != 0:
155 bits = self.em4100_decode1_partial + self.bits_pos[idx][0]
156 self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
157 self.out_ann, [10, ['%X' % bits]])
158 self.em4100_decode1_partial = 0
160 self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
161 self.out_ann, [10, ['Partial nibble']])
164 bits = self.bits_pos[idx+7][0]<<3 | self.bits_pos[idx+9][0]<<2 | \
165 self.bits_pos[idx+10][0]<<1 | self.bits_pos[idx+11][0]<<0
166 self.put(self.bits_pos[idx+7][1], self.bits_pos[idx+11][2], self.out_ann,
168 self.put4bits(idx+13)
169 self.put4bits(idx+19)
170 bits = self.bits_pos[idx+24][0]<<3 | self.bits_pos[idx+25][0]<<2 | \
171 self.bits_pos[idx+27][0]<<1 | self.bits_pos[idx+28][0]<<0
172 self.put(self.bits_pos[idx+24][1], self.bits_pos[idx+28][2], self.out_ann,
174 self.put(self.bits_pos[idx+30][1], self.bits_pos[idx+34][2],
175 self.out_ann, [10, ['EM4100 trailer']])
177 def get_32_bits(self, idx):
178 return self.get_8_bits(idx+27)<<24 | self.get_8_bits(idx+18)<<16 | \
179 self.get_8_bits(idx+9)<<8 | self.get_8_bits(idx)
181 def get_8_bits(self, idx):
183 for i in range(0, 8):
185 retval |= self.bits_pos[i+idx][0]
188 def get_3_bits(self, idx):
189 return self.bits_pos[idx][0]<<2 | self.bits_pos[idx+1][0]<<1 | \
190 self.bits_pos[idx+2][0]
192 def get_4_bits(self, idx):
193 return self.bits_pos[idx][0]<<0 | self.bits_pos[idx+1][0]<<1 | \
194 self.bits_pos[idx+2][0]<<2 | self.bits_pos[idx+3][0]<<3
196 def print_row_parity(self, idx, length):
198 for i in range(0, length):
199 parity += self.bits_pos[i+idx][0]
200 parity = parity & 0x1
201 if parity == self.bits_pos[idx+length][0]:
202 self.put(self.bits_pos[idx+length][1], self.bits_pos[idx+length][2], self.out_ann,
203 [5, ['Row parity OK', 'Parity OK', 'OK']])
205 self.put(self.bits_pos[idx+length][1], self.bits_pos[idx+length][2], self.out_ann,
206 [5, ['Row parity failed', 'Parity failed', 'Fail']])
208 def print_col_parity(self, idx):
209 data_1 = self.get_8_bits(idx)
210 data_2 = self.get_8_bits(idx+9)
211 data_3 = self.get_8_bits(idx+9+9)
212 data_4 = self.get_8_bits(idx+9+9+9)
213 col_par = self.get_8_bits(idx+9+9+9+9)
214 col_par_calc = data_1^data_2^data_3^data_4
216 if col_par == col_par_calc:
217 self.put(self.bits_pos[idx+9+9+9+9][1], self.bits_pos[idx+9+9+9+9+7][2], self.out_ann,
218 [5, ['Column parity OK', 'Parity OK', 'OK']])
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 failed', 'Parity failed', 'Fail']])
223 def print_8bit_data(self, idx):
224 data = self.get_8_bits(idx)
225 self.put(self.bits_pos[idx][1], self.bits_pos[idx+7][2], self.out_ann,
226 [9, ['Data' + ': %X' % data, '%X' % data]])
228 def put_fields(self):
229 if self.bit_nr == 50:
230 self.put(self.bits_pos[0][1], self.bits_pos[0][2], self.out_ann,
232 self.put(self.bits_pos[1][1], self.bits_pos[4][2], self.out_ann,
233 [4, ['Command', 'Cmd', 'C']])
234 self.put(self.bits_pos[5][1], self.bits_pos[49][2], self.out_ann,
235 [4, ['Password', 'Passwd', 'Pass', 'P']])
237 cmd = self.get_3_bits(1)
238 self.put(self.bits_pos[1][1], self.bits_pos[3][2], self.out_ann,
239 [5, [self.cmds[cmd]]])
240 self.print_row_parity(1, 3)
243 self.print_8bit_data(5)
244 self.print_row_parity(5, 8)
245 self.print_8bit_data(14)
246 self.print_row_parity(14, 8)
247 self.print_8bit_data(23)
248 self.print_row_parity(23, 8)
249 self.print_8bit_data(32)
250 self.print_row_parity(32, 8)
251 self.print_col_parity(5)
252 if self.bits_pos[49][0] == 0:
253 self.put(self.bits_pos[49][1], self.bits_pos[49][2], self.out_ann,
254 [5, ['Stop bit', 'Stop', 'SB']])
256 self.put(self.bits_pos[49][1], self.bits_pos[49][2], self.out_ann,
257 [5, ['Stop bit error', 'Error']])
260 password = self.get_32_bits(5)
261 self.put(self.bits_pos[12][1], self.bits_pos[46][2], self.out_ann,
262 [10, ['Login password: %X' % password]])
264 if self.bit_nr == 57:
265 self.put(self.bits_pos[0][1], self.bits_pos[0][2], self.out_ann,
266 [4, ['Logic zero', 'LZ']])
267 self.put(self.bits_pos[1][1], self.bits_pos[4][2], self.out_ann,
268 [4, ['Command', 'Cmd', 'C']])
269 self.put(self.bits_pos[5][1], self.bits_pos[11][2], self.out_ann,
270 [4, ['Address', 'Addr', 'A']])
271 self.put(self.bits_pos[12][1], self.bits_pos[56][2], self.out_ann,
272 [4, ['Data', 'Da', 'D']])
275 cmd = self.get_3_bits(1)
276 self.put(self.bits_pos[1][1], self.bits_pos[3][2], self.out_ann,
277 [5, [self.cmds[cmd]]])
278 self.print_row_parity(1, 3)
281 addr = self.get_4_bits(5)
282 self.put(self.bits_pos[5][1], self.bits_pos[8][2], self.out_ann,
283 [9, ['Addr' + ': %d' % addr, '%d' % addr]])
284 self.put(self.bits_pos[9][1], self.bits_pos[10][2], self.out_ann,
285 [5, ['Zero bits', 'ZB']])
286 self.print_row_parity(5, 6)
288 self.print_8bit_data(12)
289 self.print_row_parity(12, 8)
290 self.print_8bit_data(21)
291 self.print_row_parity(21, 8)
292 self.print_8bit_data(30)
293 self.print_row_parity(30, 8)
294 self.print_8bit_data(39)
295 self.print_row_parity(39, 8)
296 self.print_col_parity(12)
297 if self.bits_pos[56][0] == 0:
298 self.put(self.bits_pos[56][1], self.bits_pos[56][2], self.out_ann,
299 [5, ['Stop bit', 'Stop', 'SB']])
301 self.put(self.bits_pos[56][1], self.bits_pos[56][2], self.out_ann,
302 [5, ['Stop bit error', 'Error']])
305 self.decode_config(12)
308 password = self.get_32_bits(12)
309 self.put(self.bits_pos[12][1], self.bits_pos[46][2], self.out_ann,
310 [10, ['Write password: %X' % password]])
312 # If we are programming EM4100 data we can decode it halfway.
313 if addr == 5 and self.options['em4100_decode'] == 'on':
314 self.em4100_decode1(12)
315 if addr == 6 and self.options['em4100_decode'] == 'on':
316 self.em4100_decode2(12)
320 def add_bits_pos(self, bit, ss_bit, es_bit):
322 self.bits_pos[self.bit_nr][0] = bit
323 self.bits_pos[self.bit_nr][1] = ss_bit
324 self.bits_pos[self.bit_nr][2] = es_bit
327 def decode(self, ss, es, data):
328 if not self.samplerate:
329 raise SamplerateError('Cannot decode without samplerate.')
330 for (self.samplenum, (pin,)) in data:
331 # Ignore identical samples early on (for performance reasons).
332 if self.oldpin == pin:
335 if self.oldpin is None:
337 self.last_samplenum = self.samplenum
338 self.oldsamplenum = 0
340 self.gap_detected = 0
344 if self.oldpin != pin:
345 pl = self.samplenum - self.oldsamplenum
347 samples = self.samplenum - self.last_samplenum
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:
353 self.gap_detected = 0
354 self.state = 'FFS_SEARCH'
355 self.put(self.old_gap_end, self.last_samplenum,
356 self.out_ann, [3, ['Write mode exit']])
359 if self.state == 'FFS_SEARCH':
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'
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:
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)
388 self.old_gap_end = self.samplenum
390 if self.state == 'SKIP':
391 self.state = 'FFS_SEARCH'
393 self.oldsamplenum = self.samplenum
394 self.last_samplenum = self.samplenum