]> sigrok.org Git - libsigrokdecode.git/blob - decoders/em4305/pd.py
cda9b3043c5910984dbc1a78026d260e04d7f6ce
[libsigrokdecode.git] / decoders / em4305 / pd.py
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
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
18 ##
19
20 import sigrokdecode as srd
21
22 class SamplerateError(Exception):
23     pass
24
25 class Decoder(srd.Decoder):
26     api_version = 2
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']
34     channels = (
35         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
36     )
37     options = (
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')},
46     )
47     annotations = (
48         ('bit_value', 'Bit value'),
49         ('first_field_stop', 'First field stop'),
50         ('write_gap', 'Write gap'),
51         ('write_mode_exit', 'Write mode exit'),
52         ('bit', 'Bit'),
53         ('opcode', 'Opcode'),
54         ('lock', 'Lock'),
55         ('data', 'Data'),
56         ('password', 'Password'),
57         ('address', 'Address'),
58         ('bitrate', 'Bitrate'),
59     )
60     annotation_rows = (
61         ('bits', 'Bits', (0,)),
62         ('structure', 'Structure', (1, 2, 3, 4)),
63         ('fields', 'Fields', (5, 6, 7, 8, 9)),
64         ('decode', 'Decode', (10,)),
65     )
66
67     def __init__(self):
68         self.samplerate = None
69         self.oldpin = 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']
79
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
90
91     def start(self):
92         self.out_ann = self.register(srd.OUTPUT_ANN)
93
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']])
131
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,
136                  [10, ['%X' % bits]])
137
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,
145                [10, ['%X' % bits]])
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']])
152
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
159         else:
160             self.put(self.bits_pos[idx][1], self.bits_pos[idx][2],
161                      self.out_ann, [10, ['Partial nibble']])
162
163         self.put4bits(idx+2)
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,
167                [10, ['%X' % bits]])
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,
173                [10, ['%X' % bits]])
174         self.put(self.bits_pos[idx+30][1], self.bits_pos[idx+34][2],
175                  self.out_ann, [10, ['EM4100 trailer']])
176
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)
180
181     def get_8_bits(self, idx):
182         retval = 0
183         for i in range(0, 8):
184             retval <<= 1
185             retval |= self.bits_pos[i+idx][0]
186         return retval
187
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]
191
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
195
196     def print_row_parity(self, idx, length):
197         parity = 0
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']])
204         else:
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']])
207
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
215
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']])
219         else:
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']])
222
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]])
227
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,
231                      [4, ['Logic zero']])
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']])
236             # Get command.
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)
241
242             # Print data.
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']])
255             else:
256                 self.put(self.bits_pos[49][1], self.bits_pos[49][2], self.out_ann,
257                          [5, ['Stop bit error', 'Error']])
258
259             if cmd == 1:
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]])
263
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']])
273
274             # Get command.
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)
279
280             # Get address.
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)
287             # Print data.
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']])
300             else:
301                 self.put(self.bits_pos[56][1], self.bits_pos[56][2], self.out_ann,
302                          [5, ['Stop bit error', 'Error']])
303
304             if addr == 4:
305                 self.decode_config(12)
306
307             if addr == 2:
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]])
311
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)
317
318         self.bit_nr = 0
319
320     def add_bits_pos(self, bit, ss_bit, es_bit):
321         if self.bit_nr < 70:
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
325             self.bit_nr += 1
326
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:
333                 continue
334
335             if self.oldpin is None:
336                 self.oldpin = pin
337                 self.last_samplenum = self.samplenum
338                 self.oldsamplenum = 0
339                 self.old_gap_end = 0
340                 self.gap_detected = 0
341                 self.bit_nr = 0
342                 continue
343
344             if self.oldpin != pin:
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:
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']])
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
395                 self.oldpin = pin