]> sigrok.org Git - libsigrokdecode.git/blob - decoders/ps2/pd.py
Add initial version of PS/2 decoder
[libsigrokdecode.git] / decoders / ps2 / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2016 Daniel Schulte <trilader@schroedingers-bit.net>
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, write to the Free Software
18 ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19 ##
20
21 import sigrokdecode as srd
22
23 class Ann:
24     START, STOP, PARITY, WORD = range(4)
25
26 class Decoder(srd.Decoder):
27     api_version = 2
28     id = 'ps2'
29     name = 'PS/2'
30     longname = 'PS/2'
31     desc = 'PS/2 keyboard/mouse interface.'
32     license = 'gplv2+'
33     inputs = ['logic']
34     outputs = ['ps2']
35     optional_channels = (
36         {'id': 'clk', 'name': 'Clock', 'desc': 'Clock line'},
37         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
38     )
39     annotations = (
40         ('start-bit', 'Start bit'),
41         ('stop-bit', 'Stop bit'),
42         ('parity-bit', 'Parity bit'),
43         ('word', 'Word')
44     )
45
46     def __init__(self):
47         self.bits = []
48         self.prev_pins = None
49         self.prev_clock = None
50         self.samplenum = 0
51         self.ss_word = None
52         self.clock_was_high = False
53
54     def start(self):
55         self.out_ann = self.register(srd.OUTPUT_ANN)
56
57     def handle_bits(self, datapin):
58         # Ignore non start condition bits (useful during keyboard init).
59         if len(self.bits) == 0 and datapin == 1:
60             return
61
62         # If this is the first bit in a word, save its sample number.
63         if len(self.bits) == 0:
64             self.ss_word = self.samplenum
65
66         self.bits.append(datapin)
67
68         # Find all 11 bits. Start + 8 data + odd parity + stop.
69         if len(self.bits) < 11:
70             return
71
72         # Extract data word.
73         word = 0
74         for i in range(8):
75             word |= (self.bits[i + 1] << i)
76
77         bit_start, bit_stop, bit_parity = self.bits[0], \
78             self.bits[10], self.bits[9]
79
80         bitstring = ''.join([str(i) for i in self.bits])
81         parity_ok = (bin(word).count('1') + bit_parity) % 2 == 1
82
83         if bit_start == 0 and bit_stop == 1 and parity_ok:
84             self.put(self.ss_word, self.samplenum, self.out_ann, [Ann.WORD,
85                      ['OK: %X (%s)' % (word, bitstring)]])
86         else:
87             self.put(self.ss_word, self.samplenum, self.out_ann, [Ann.WORD,
88                      ['Fail: %X (%s)' % (word, bitstring)]])
89
90         self.bits, self.ss_word = [], 0
91
92     def find_clk_edge(self, clock_pin, data_pin):
93         # Ignore sample if the clock pin hasn't changed.
94         if clock_pin == self.prev_clock:
95             return
96         self.prev_clock = clock_pin
97
98         # Sample on falling clock edge.
99         if clock_pin == 1:
100             return
101
102         # Found the correct clock edge, now get the bits.
103         self.handle_bits(data_pin)
104
105     def decode(self, ss, es, data):
106         for (self.samplenum, pins) in data:
107             clock_pin, data_pin = pins[0], pins[1]
108
109             # Ignore identical samples.
110             if self.prev_pins == pins:
111                 continue
112             self.prev_pins = pins
113
114             if clock_pin == 0 and not self.clock_was_high:
115                 continue
116             self.clock_was_high = True
117
118             self.find_clk_edge(clock_pin, data_pin)