Skip to content

Commit 7d8409c

Browse files
committed
ps2: Added support for host->device communication
Detects if host or device is initiaing communication Outputs byte for stacked decoder Outputs human-readable "binary" for logging
1 parent 73cb546 commit 7d8409c

File tree

2 files changed

+109
-65
lines changed

2 files changed

+109
-65
lines changed

decoders/ps2/__init__.py

100644100755
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
## This file is part of the libsigrokdecode project.
33
##
44
## Copyright (C) 2016 Daniel Schulte <[email protected]>
5+
## Copyright (C) 2023 Marshal Horn <[email protected]>
56
##
67
## This program is free software; you can redistribute it and/or modify
78
## it under the terms of the GNU General Public License as published by
@@ -18,9 +19,11 @@
1819
##
1920

2021
'''
21-
This protocol decoder can decode PS/2 device -> host communication.
22+
This protocol decoder can decode PS/2 device -> host communication \
23+
and host -> device communication.
2224
23-
Host -> device communication is currently not supported.
25+
To interpret the data, please stack the appropriate keyboard or mouse \
26+
decoder
2427
'''
2528

2629
from .pd import Decoder

decoders/ps2/pd.py

100644100755
Lines changed: 104 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
## This file is part of the libsigrokdecode project.
33
##
44
## Copyright (C) 2016 Daniel Schulte <[email protected]>
5+
## Copyright (C) 2023 Marshal Horn <[email protected]>
56
##
67
## This program is free software; you can redistribute it and/or modify
78
## it under the terms of the GNU General Public License as published by
@@ -18,22 +19,33 @@
1819
##
1920

2021
import sigrokdecode as srd
21-
from collections import namedtuple
2222

2323
class Ann:
24-
BIT, START, STOP, PARITY_OK, PARITY_ERR, DATA, WORD = range(7)
24+
BIT, START, WORD, PARITY_OK, PARITY_ERR, STOP, ACK, NACK = range(8)
25+
26+
class Bit:
27+
def __init__(self, val, ss, es):
28+
self.val = val
29+
self.ss = ss
30+
self.es = es
31+
32+
class Ps2Packet:
33+
def __init__(self, val, host=False, pok=False, ack=False):
34+
self.val = val #byte value
35+
self.host = host #Host transmissions
36+
self.pok = pok #Parity ok
37+
self.ack = ack #Acknowlege ok for host transmission.
2538

26-
Bit = namedtuple('Bit', 'val ss es')
2739

2840
class Decoder(srd.Decoder):
2941
api_version = 3
3042
id = 'ps2'
3143
name = 'PS/2'
3244
longname = 'PS/2'
33-
desc = 'PS/2 keyboard/mouse interface.'
45+
desc = 'PS/2 packet interface used by PC keyboards and mice'
3446
license = 'gplv2+'
3547
inputs = ['logic']
36-
outputs = []
48+
outputs = ['ps2_packet']
3749
tags = ['PC']
3850
channels = (
3951
{'id': 'clk', 'name': 'Clock', 'desc': 'Clock line'},
@@ -42,15 +54,22 @@ class Decoder(srd.Decoder):
4254
annotations = (
4355
('bit', 'Bit'),
4456
('start-bit', 'Start bit'),
45-
('stop-bit', 'Stop bit'),
57+
('word', 'Word'),
4658
('parity-ok', 'Parity OK bit'),
4759
('parity-err', 'Parity error bit'),
48-
('data-bit', 'Data bit'),
60+
('stop-bit', 'Stop bit'),
61+
('ack', 'Acknowledge'),
62+
('nack', 'Not Acknowledge'),
63+
('start-bit', 'Start bit'),
4964
('word', 'Word'),
65+
('parity-ok', 'Parity OK bit'),
66+
('parity-err', 'Parity error bit'),
67+
('stop-bit', 'Stop bit'),
5068
)
5169
annotation_rows = (
5270
('bits', 'Bits', (0,)),
53-
('fields', 'Fields', (1, 2, 3, 4, 5, 6)),
71+
('fields', 'Device', (1,2,3,4,5,6,7,)),
72+
('host', 'Host', (8,9,10,11,12)),
5473
)
5574

5675
def __init__(self):
@@ -62,65 +81,87 @@ def reset(self):
6281

6382
def start(self):
6483
self.out_ann = self.register(srd.OUTPUT_ANN)
65-
66-
def putb(self, bit, ann_idx):
67-
b = self.bits[bit]
68-
self.put(b.ss, b.es, self.out_ann, [ann_idx, [str(b.val)]])
69-
70-
def putx(self, bit, ann):
84+
self.out_py = self.register(srd.OUTPUT_PYTHON)
85+
86+
def metadata(self,key,value):
87+
if key == srd.SRD_CONF_SAMPLERATE:
88+
self.samplerate = value
89+
90+
def get_bits(self, n, edge:'r', timeout=100e-6):
91+
max_period = int(timeout * self.samplerate) + 1
92+
for i in range(n):
93+
_, dat = self.wait([{0:edge},{'skip':max_period}])
94+
if not self.matched[0]:
95+
break #Timed out
96+
self.bits.append(Bit(dat, self.samplenum, self.samplenum+max_period))
97+
if i>0: #Fix the ending period
98+
self.bits[i-1].es = self.samplenum
99+
if len(self.bits) == n:
100+
self.wait([{0:'r'},{'skip':max_period}])
101+
self.bits[-1].es = self.samplenum
102+
self.bitcount = len(self.bits)
103+
104+
def putx(self, bit, ann, host=False):
105+
if host:
106+
ann[0] += 7 #host annotation offset
71107
self.put(self.bits[bit].ss, self.bits[bit].es, self.out_ann, ann)
72108

73-
def handle_bits(self, datapin):
74-
# Ignore non start condition bits (useful during keyboard init).
75-
if self.bitcount == 0 and datapin == 1:
76-
return
77-
78-
# Store individual bits and their start/end samplenumbers.
79-
self.bits.append(Bit(datapin, self.samplenum, self.samplenum))
80-
81-
# Fix up end sample numbers of the bits.
82-
if self.bitcount > 0:
83-
b = self.bits[self.bitcount - 1]
84-
self.bits[self.bitcount - 1] = Bit(b.val, b.ss, self.samplenum)
85-
if self.bitcount == 11:
86-
self.bitwidth = self.bits[1].es - self.bits[2].es
87-
b = self.bits[-1]
88-
self.bits[-1] = Bit(b.val, b.ss, b.es + self.bitwidth)
89-
90-
# Find all 11 bits. Start + 8 data + odd parity + stop.
91-
if self.bitcount < 11:
92-
self.bitcount += 1
93-
return
94-
95-
# Extract data word.
96-
word = 0
97-
for i in range(8):
98-
word |= (self.bits[i + 1].val << i)
109+
def handle_bits(self, host=False):
110+
# Annotate individual bits
111+
for b in self.bits:
112+
self.put(b.ss, b.es, self.out_ann, [Ann.BIT, [str(b.val)]])
113+
114+
packet = None
115+
# Annotate start bit
116+
if self.bitcount > 8:
117+
self.putx(0, [Ann.START, ['Start bit', 'Start', 'S']], host)
118+
# Annotate the data word
119+
word = 0
120+
for i in range(8):
121+
word |= (self.bits[i + 1].val << i)
122+
self.put(self.bits[1].ss, self.bits[8].es, self.out_ann,
123+
[Ann.WORD+7 if host else Ann.WORD,
124+
['Data: %02x' % word, 'D: %02x' % word, '%02x' % word]])
125+
packet = Ps2Packet(val = word, host = host)
99126

100127
# Calculate parity.
101-
parity_ok = (bin(word).count('1') + self.bits[9].val) % 2 == 1
102-
103-
# Emit annotations.
104-
for i in range(11):
105-
self.putb(i, Ann.BIT)
106-
self.putx(0, [Ann.START, ['Start bit', 'Start', 'S']])
107-
self.put(self.bits[1].ss, self.bits[8].es, self.out_ann, [Ann.WORD,
108-
['Data: %02x' % word, 'D: %02x' % word, '%02x' % word]])
109-
if parity_ok:
110-
self.putx(9, [Ann.PARITY_OK, ['Parity OK', 'Par OK', 'P']])
111-
else:
112-
self.putx(9, [Ann.PARITY_ERR, ['Parity error', 'Par err', 'PE']])
113-
self.putx(10, [Ann.STOP, ['Stop bit', 'Stop', 'St', 'T']])
114-
115-
self.bits, self.bitcount = [], 0
128+
if self.bitcount > 9:
129+
parity_ok = 0
130+
for bit in self.bits[1:10]:
131+
parity_ok ^= bit.val
132+
if bool(parity_ok):
133+
self.putx(9, [Ann.PARITY_OK, ['Parity OK', 'Par OK', 'P']], host)
134+
packet.pok = True #Defaults to false in case packet was interrupted
135+
else:
136+
self.putx(9, [Ann.PARITY_ERR, ['Parity error', 'Par err', 'PE']], host)
137+
138+
# Annotate stop bit
139+
if self.bitcount > 10:
140+
self.putx(10, [Ann.STOP+7 if host else Ann.STOP, ['Stop bit', 'Stop', 'St', 'T']])
141+
# Annotate ACK
142+
if host and self.bitcount > 11:
143+
if self.bits[11].val == 0:
144+
self.putx(11, [Ann.ACK, ['Acknowledge', 'Ack', 'A']])
145+
else:
146+
self.putx(11, [Ann.NACK, ['Not Acknowledge', 'Nack', 'N']])
147+
packet.ack = not bool(self.bits[11].val)
148+
149+
if(packet):
150+
self.put(self.bits[0].ss, self.bits[-1].ss, self.out_py,packet)
151+
self.reset()
152+
116153

117154
def decode(self):
155+
if not self.samplerate:
156+
raise SamplerateError("Cannot decode without samplerate")
118157
while True:
119-
# Sample data bits on the falling clock edge (assume the device
120-
# is the transmitter). Expect the data byte transmission to end
121-
# at the rising clock edge. Cope with the absence of host activity.
122-
_, data_pin = self.wait({0: 'f'})
123-
self.handle_bits(data_pin)
124-
if self.bitcount == 1 + 8 + 1 + 1:
125-
_, data_pin = self.wait({0: 'r'})
126-
self.handle_bits(data_pin)
158+
# Falling edge of data indicates start condition
159+
clk, dat = self.wait({1: 'l'})
160+
host = not bool(clk)
161+
if host:
162+
# Host emits bits on rising clk edge
163+
self.get_bits(12, 'r')
164+
else:
165+
# Client emits data on falling edge
166+
self.get_bits(11, 'f')
167+
self.handle_bits(host=host)

0 commit comments

Comments
 (0)