]> sigrok.org Git - libsigrokdecode.git/blob - decoders/sle44xx/pd.py
sle44xx: introduce decoder for Siemens memory cards
[libsigrokdecode.git] / decoders / sle44xx / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2019 Federico Cerutti <federico@ceres-c.it>
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 '''
23 OUTPUT_PYTHON format:
24
25 Packet:
26 [<ptype>, <pdata>]
27
28 <ptype>:
29  - 'RESET'  (Reset/Abort condition)
30  - 'ATR'    (ATR data from card)
31  - 'CMD'    (Command from reader)
32  - 'DATA'   (Data from card)
33
34 <pdata> is the data to/from the card
35 For 'RESET' <pdata> is None.
36 '''
37
38 # CMD: [annotation-type-index, long annotation, short annotation]
39 proto = {
40     'RESET':           [0, 'Reset',         'R'],
41     'ATR':             [1, 'ATR',           'ATR'],
42     'CMD':             [2, 'Command',       'C'],
43     'DATA':            [3, 'Data',          'D'],
44 }
45
46 class Decoder(srd.Decoder):
47     api_version = 3
48     id = 'sle44xx'
49     name = 'SLE 44xx'
50     longname = 'SLE44xx protocol'
51     desc = 'SLE 4418/28/32/42 memory card serial protocol'
52     license = 'gplv2+'
53     inputs = ['logic']
54     outputs = ['sle44xx']
55     channels = (
56         {'id': 'rst', 'name': 'RST', 'desc': 'Reset line'},
57         {'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'},
58         {'id': 'io', 'name': 'I/O', 'desc': 'I/O data line'},
59     )
60     annotations = (
61         ('reset', 'Reset'),
62         ('atr', 'ATR'),
63         ('cmd', 'Command'),
64         ('data', 'Data exchange'),
65         ('bit', 'Bit'),
66     )
67     annotation_rows = (
68         ('bits', 'Bits', (4,)),
69         ('data', 'Data', (1, 2, 3)),
70         ('interrupts', 'Interrupts', (0,)),
71     )
72     binary = (
73         ('send-data', 'Send data'),
74     )
75
76     def __init__(self):
77         self.reset()
78
79     def reset(self):
80         self.ss = self.es = self.ss_byte = -1
81         self.bitcount = 0
82         self.databyte = 0
83         self.bits = []
84         self.cmd = 'RESET'
85
86     def metadata(self, key, value):
87         if key == srd.SRD_CONF_SAMPLERATE:
88             self.samplerate = value
89
90     def start(self):
91         self.out_python = self.register(srd.OUTPUT_PYTHON)
92         self.out_ann = self.register(srd.OUTPUT_ANN)
93         self.out_binary = self.register(srd.OUTPUT_BINARY)
94
95     def putx(self, data):
96         self.put(self.ss, self.es, self.out_ann, data)
97
98     def putp(self, data):
99         self.put(self.ss, self.es, self.out_python, data)
100
101     def putb(self, data):
102         self.put(self.ss, self.es, self.out_binary, data)
103
104     def handle_reset(self, pins):
105         self.ss, self.es = self.samplenum, self.samplenum
106         cmd = 'RESET' # No need to set the global self.cmd as this command is atomic
107         self.putp([cmd, None])
108         self.putx([proto[cmd][0], proto[cmd][1:]])
109         self.bitcount = self.databyte = 0
110         self.bits = []
111         self.cmd = 'ATR' # Next data bytes will be ATR
112
113     def handle_command(self, pins):
114         rst, clk, io = pins
115         self.ss, self.es = self.samplenum, self.samplenum
116         # If I/O is rising -> command START
117         # if I/O is falling -> command STOP and response data incoming
118         self.cmd = 'CMD' if (io == 0) else 'DATA'
119         self.bitcount = self.databyte = 0
120         self.bits = []
121
122     # Gather 8 bits of data
123     def handle_data(self, pins):
124         rst, clk, io = pins
125
126         # Data is transmitted LSB-first.
127         self.databyte |= (io << self.bitcount)
128
129         # Remember the start of the first data/address bit.
130         if self.bitcount == 0:
131             self.ss_byte = self.samplenum
132
133         # Store individual bits and their start/end samplenumbers.
134         # In the list, index 0 represents the LSB (SLE44xx transmits LSB-first).
135         self.bits.insert(0, [io, self.samplenum, self.samplenum])
136         if self.bitcount > 0:
137             self.bits[1][2] = self.samplenum
138         if self.bitcount == 7:
139             self.bitwidth = self.bits[1][2] - self.bits[2][2]
140             self.bits[0][2] += self.bitwidth
141
142         # Return if we haven't collected all 8 bits, yet.
143         if self.bitcount < 7:
144             self.bitcount += 1
145             return
146
147         self.ss, self.es = self.ss_byte, self.samplenum + self.bitwidth
148
149         self.putb([0, bytes([self.databyte])])
150
151         for bit in self.bits:
152             self.put(bit[1], bit[2], self.out_ann, [4, ['%d' % bit[0]]])
153
154         self.putx([proto[self.cmd][0], ['%s: %02X' % (proto[self.cmd][1], self.databyte),
155                    '%s: %02X' % (proto[self.cmd][2], self.databyte), '%02X' % self.databyte]])
156
157         # Done with this packet.
158         self.bitcount = self.databyte = 0
159         self.bits = []
160
161     def decode(self):
162         while True:
163             pins = self.wait([{0: 'r'}, {0: 'l', 1: 'r'}, {1: 'h', 2: 'f'}, {1: 'h', 2: 'r'}])
164             if self.matched[0]: # RESET condition (R): RST = rising
165                 self.handle_reset(pins)
166             elif self.matched[1]: # Incoming data (D): RST = low, CLK = rising.
167                 self.handle_data(pins)
168             elif self.matched[2]: # Command mode START: CLK = high, I/O = falling.
169                 self.handle_command(pins)
170             elif self.matched[3]: # Command mode STOP: CLK = high, I/O = rising.
171                 self.handle_command(pins)