]> sigrok.org Git - libsigrokdecode.git/blob - decoders/i2cfilter/pd.py
i2cfilter: rephrase decoder implementation for maintainability
[libsigrokdecode.git] / decoders / i2cfilter / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2012 Bert Vermeulen <bert@biot.com>
5 ## Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
6 ##
7 ## This program is free software; you can redistribute it and/or modify
8 ## it under the terms of the GNU General Public License as published by
9 ## the Free Software Foundation; either version 3 of the License, or
10 ## (at your option) any later version.
11 ##
12 ## This program is distributed in the hope that it will be useful,
13 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ## GNU General Public License for more details.
16 ##
17 ## You should have received a copy of the GNU General Public License
18 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
19 ##
20
21 # TODO
22 # - Accept other slave address forms than decimal numbers?
23 # - Support for filtering out multiple slave/direction pairs?
24 # - Support 10bit slave addresses?
25
26 import copy
27 import sigrokdecode as srd
28
29 class Decoder(srd.Decoder):
30     api_version = 3
31     id = 'i2cfilter'
32     name = 'I²C filter'
33     longname = 'I²C filter'
34     desc = 'Filter out addresses/directions in an I²C stream.'
35     license = 'gplv3+'
36     inputs = ['i2c']
37     outputs = ['i2c']
38     tags = ['Util']
39     options = (
40         {'id': 'address', 'desc': 'Slave address to filter (decimal)',
41             'default': 0},
42         {'id': 'direction', 'desc': 'Direction to filter', 'default': 'both',
43             'values': ('read', 'write', 'both')}
44     )
45
46     def __init__(self):
47         self.reset()
48
49     def reset(self):
50         self.seen_packets = []
51         self.do_forward = None
52
53     def start(self):
54         self.out_python = self.register(srd.OUTPUT_PYTHON, proto_id='i2c')
55         if self.options['address'] not in range(0, 127 + 1):
56             raise Exception('Invalid slave (must be 0..127).')
57         self.want_addrs = []
58         if self.options['address']:
59             self.want_addrs.append(self.options['address'])
60         self.want_dir = {
61             'read': 'READ', 'write': 'WRITE',
62         }.get(self.options['direction'], None)
63
64     def _need_to_forward(self, slave_addr, direction):
65         if self.want_addrs and slave_addr not in self.want_addrs:
66             return False
67         if self.want_dir and direction != self.want_dir:
68             return False
69         return True
70
71     # Accumulate observed I2C packets until a STOP or REPEATED START
72     # condition is seen. These are conditions where transfers end or
73     # where direction potentially changes. Forward all previously
74     # accumulated traffic if it passes the slave address and direction
75     # filter. This assumes that the slave address as well as the read
76     # or write direction was part of the observed traffic. There should
77     # be no surprise when incomplete traffic does not match the filter
78     # condition.
79     def decode(self, ss, es, data):
80
81         # Unconditionally accumulate every lower layer packet we see.
82         # Keep deep copies for later, only reference caller's values
83         # as long as this .decode() invocation executes.
84         self.seen_packets.append([ss, es, copy.deepcopy(data)])
85         cmd, _ = data
86
87         # Check the slave address and transfer direction early when
88         # we see them. Keep accumulating packets while it's already
89         # known here whether to forward them. This simplifies other
90         # code paths. Including future handling of 10bit addresses.
91         if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
92             direction = cmd[len('ADDRESS '):]
93             _, slave_addr = data
94             self.do_forward = self._need_to_forward(slave_addr, direction)
95             return
96
97         # Forward previously accumulated packets as we see their
98         # completion, and when they pass the filter condition. Prepare
99         # to handle the next transfer (the next read/write part of it).
100         if cmd in ('STOP', 'START REPEAT'):
101             if self.do_forward:
102                 for ss, es, data in self.seen_packets:
103                     self.put(ss, es, self.out_python, data)
104             self.seen_packets.clear()
105             self.do_forward = None
106             return