Skip to content

Commit be0442c

Browse files
committed
ps2: Added mouse and keyboard stacked decoders
Mouse decoder interprets the 3-byte packets for movement and clicking, but ignores the configuration commands from the host. Keyboard decoder interprets press and release messages for each key but does not keep track of state (capslock, shift, etc).
1 parent 7d8409c commit be0442c

File tree

5 files changed

+366
-0
lines changed

5 files changed

+366
-0
lines changed

decoders/ps2_keyboard/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
##
2+
## This file is part of the libsigrokdecode project.
3+
##
4+
## Copyright (C) 2023 Marshal Horn <[email protected]>
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+
'''
21+
This protocol decoder can decode PS/2 keyboard commands.
22+
It should be stacked on the PS/2 packet decoder.
23+
'''
24+
25+
from .pd import Decoder

decoders/ps2_keyboard/pd.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
##
2+
## This file is part of the libsigrokdecode project.
3+
##
4+
## Copyright (C) 2023 Marshal Horn <[email protected]>
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+
from .sc import key_decode
22+
23+
class Ps2Packet:
24+
def __init__(self, val, host=False, pok=False, ack=False):
25+
self.val = val #byte value
26+
self.host = host #Host transmissions
27+
self.pok = pok #Parity ok
28+
self.ack = ack #Acknowlege ok for host transmission.
29+
30+
class Ann:
31+
PRESS,RELEASE,ACK = range(3)
32+
33+
class Decoder(srd.Decoder):
34+
api_version = 3
35+
id = 'keyboard'
36+
name = 'PS/2 Keyboard'
37+
longname = 'PS/2 Keyboard'
38+
desc = 'PS/2 keyboard interface.'
39+
license = 'gplv2+'
40+
inputs = ['ps2_packet']
41+
outputs = []
42+
tags = ['PC']
43+
binary = (
44+
('Keys', 'Key presses'),
45+
)
46+
annotations = (
47+
('Press', 'Key pressed'),
48+
('Release', 'Key released'),
49+
('Ack', 'Acknowledge'),
50+
)
51+
annotation_rows = (
52+
('keys', 'key presses and releases',(0,1,2)),
53+
)
54+
55+
def __init__(self):
56+
self.reset()
57+
58+
def reset(self):
59+
self.sw = 0 #for switch statement
60+
self.ann = Ann.PRESS #defualt to keypress
61+
self.extended = False
62+
63+
def start(self):
64+
self.out_binary = self.register(srd.OUTPUT_BINARY)
65+
self.out_ann = self.register(srd.OUTPUT_ANN)
66+
67+
def decode(self,startsample,endsample,data):
68+
if data.host:
69+
# Ignore host commands or interrupted keycodes
70+
self.reset()
71+
return
72+
if self.sw < 1:
73+
self.ss = startsample
74+
self.sw = 1
75+
if self.sw < 2:
76+
if data.val == 0xF0: #Break code
77+
self.ann = Ann.RELEASE
78+
return
79+
elif data.val == 0xE0: #Extended character
80+
self.extended = True
81+
return
82+
elif data.val == 0xFA: #Acknowledge code
83+
c = ['Acknowledge','ACK']
84+
self.ann = Ann.ACK
85+
self.sw = 4
86+
if self.sw < 3:
87+
c = key_decode(data.val, self.extended)
88+
89+
self.put(self.ss,endsample,self.out_ann,[self.ann,c])
90+
if self.ann == Ann.PRESS:
91+
self.put(self.ss,endsample,self.out_binary,[0,c[-1].encode('UTF-8')])
92+
self.reset()
93+

decoders/ps2_keyboard/sc.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#A list of scancodes for the PS2 keyboard
2+
ext = {
3+
0x1F:'L Sup',
4+
0x14:'R Ctrl',
5+
0x27:'R Sup',
6+
0x11:'R Alt',
7+
0x2F:'Menu',
8+
0x12:'PrtScr',
9+
0x7C:'SysRq',
10+
0x70:'Insert',
11+
0x6C:'Home',
12+
0x7D:'Pg Up',
13+
0x71:'Delete',
14+
0x69:'End',
15+
0x7A:'Pg Dn',
16+
0x75:['Up arrow','^'],
17+
0x6B:['Left arrow','Left','<-'],
18+
0x74:['Right arrow','Right','->'],
19+
0x72:['Down arrow','Down','v'],
20+
0x4A:['KP /','/'],
21+
0x5A:['KP Ent','\n'],
22+
}
23+
24+
std = {
25+
0x1C:'A',
26+
0x32:'B',
27+
0x21:'C',
28+
0x23:'D',
29+
0x24:'E',
30+
0x2B:'F',
31+
0x34:'G',
32+
0x33:'H',
33+
0x43:'I',
34+
0x3B:'J',
35+
0x42:'K',
36+
0x4B:'L',
37+
0x3A:'M',
38+
0x31:'N',
39+
0x44:'O',
40+
0x4D:'P',
41+
0x15:'Q',
42+
0x2D:'R',
43+
0x1B:'S',
44+
0x2C:'T',
45+
0x3C:'U',
46+
0x2A:'V',
47+
0x1D:'W',
48+
0x22:'X',
49+
0x35:'Y',
50+
0x1A:'Z',
51+
0x45:'0)',
52+
0x16:'1!',
53+
0x1E:'2@',
54+
0x26:'3#',
55+
0x25:'4$',
56+
0x2E:'5%',
57+
0x36:'6^',
58+
0x3D:'7&',
59+
0x3E:'8*',
60+
0x46:'9(',
61+
0x0E:'`~',
62+
0x4E:'-_',
63+
0x55:'=+',
64+
0x5D:'\|',
65+
0x66:'Backsp',
66+
0x29:['Space',' '],
67+
0x0D:['Tab','\t'],
68+
0x58:'CapsLk',
69+
0x12:'L Shft',
70+
0x14:'L Ctrl',
71+
0x11:'L Alt',
72+
0x59:'R Shft',
73+
0x5A:['Enter','\n'],
74+
0x76:'Esc',
75+
0x5:'F1',
76+
0x6:'F2',
77+
0x4:'F3',
78+
0x0C:'F4',
79+
0x3:'F5',
80+
0x0B:'F6',
81+
0x83:'F7',
82+
0x0A:'F8',
83+
0x1:'F9',
84+
0x9:'F10',
85+
0x78:'F11',
86+
0x7:'F12',
87+
0x7E:'ScrLck',
88+
}
89+
90+
91+
def key_decode(code, extended):
92+
try:
93+
c = ext[code] if extended else std[code]
94+
except KeyError:
95+
fs = '[E0%0X]' if extended else '[%0X]'
96+
c = fs % code
97+
if not isinstance(0,list):
98+
c = [c]
99+
return c

decoders/ps2_mouse/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
##
2+
## This file is part of the libsigrokdecode project.
3+
##
4+
## Copyright (C) 2023 Marshal Horn <[email protected]>
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+
'''
21+
This protocol decoder can decode PS/2 mouse commands.
22+
It should be stacked on the PS/2 packet decoder.
23+
'''
24+
25+
from .pd import Decoder

decoders/ps2_mouse/pd.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
##
2+
## This file is part of the libsigrokdecode project.
3+
##
4+
## Copyright (C) 2023 Marshal Horn <[email protected]>
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 Ps2Packet:
23+
def __init__(self, val, host=False, pok=False, ack=False):
24+
self.val = val #byte value
25+
self.host = host #Host transmissions
26+
self.pok = pok #Parity ok
27+
self.ack = ack #Acknowlege ok for host transmission.
28+
29+
class Decoder(srd.Decoder):
30+
api_version = 3
31+
id = 'mouse'
32+
name = 'PS/2 Mouse'
33+
longname = 'PS/2 Mouse'
34+
desc = 'PS/2 mouse interface.'
35+
license = 'gplv2+'
36+
inputs = ['ps2_packet']
37+
outputs = []
38+
tags = ['PC']
39+
binary = (
40+
('bytes', 'Bytes without explanation'),
41+
('movement', 'Explanation of mouse movement and clicks'),
42+
)
43+
annotations = (
44+
('Movement', 'Mouse movement packets'),
45+
)
46+
annotation_rows = (
47+
('mov', 'Mouse Movement',(0,)),
48+
)
49+
50+
def __init__(self):
51+
self.reset()
52+
53+
def reset(self):
54+
self.packets = []
55+
self.es = 0
56+
self.ss = 0
57+
58+
def start(self):
59+
self.out_binary = self.register(srd.OUTPUT_BINARY)
60+
self.out_ann = self.register(srd.OUTPUT_ANN)
61+
62+
def metadata(self,key,value):
63+
if key == srd.SRD_CONF_SAMPLERATE:
64+
self.samplerate = value
65+
66+
def mouse_movement(self):
67+
if len(self.packets) >= 3:
68+
if not self.packets[0].host:
69+
msg = ''
70+
[flags,x,y] = [p.val for p in self.packets[:3]]
71+
if flags & 1:
72+
msg += 'L'
73+
if flags & 2:
74+
msg += 'M'
75+
if flags & 4:
76+
msg += 'R'
77+
if flags & 0x10:
78+
x = x-256
79+
if flags & 0x20:
80+
y = y-256
81+
if x != 0:
82+
msg += ' X%+d' % x
83+
if flags & 0x40:
84+
msg += '!!'
85+
if y != 0:
86+
msg += ' Y%+d' % y
87+
if flags & 0x80:
88+
msg += '!!'
89+
if msg == '':
90+
msg = 'No Movement'
91+
ustring = ('\n' + msg).encode('UTF-8')
92+
self.put(self.ss,self.es,self.out_binary, [1,ustring] )
93+
self.put(self.ss,self.es,self.out_ann, [0,[msg]] )
94+
95+
96+
def print_packets(self):
97+
self.mouse_movement()
98+
tag = "Host: " if self.packets[-1].host else "Mouse:"
99+
octets = ' '.join(["%02X" % x.val for x in self.packets])
100+
unicode_string = ("\n"+tag+" "+octets).encode('UTF-8')
101+
self.put(self.ss,self.es,self.out_binary, [0,unicode_string] )
102+
self.reset()
103+
104+
def mouse_ack(self,ss,es):
105+
self.put(ss,es,self.out_binary, [0,b' ACK'] )
106+
107+
def decode(self,startsample,endsample,data):
108+
if len(self.packets) == 0:
109+
self.ss = startsample
110+
elif data.host != self.packets[-1].host:
111+
self.print_packets()
112+
self.ss = startsample #Packets were cleared, need to set startsample again
113+
if data.val == 0xFA and not data.host:
114+
#Special case: acknowledge byte from mouse
115+
self.mouse_ack(startsample,endsample)
116+
self.reset()
117+
return
118+
119+
self.packets.append(data)
120+
self.es = endsample
121+
#Mouse streaming packets are in 3s
122+
#Timing is not guaranteed because host can hold the clock at any point
123+
if len(self.packets)>2:
124+
self.print_packets()

0 commit comments

Comments
 (0)