1 //
2 // Copyright (c) 2010-2018 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Utilities;
13 
14 namespace Antmicro.Renode.Peripherals.Input
15 {
16     public class PS2Mouse : IPS2Peripheral, IRelativePositionPointerInput
17     {
PS2Mouse()18         public PS2Mouse()
19         {
20             data = new Queue<byte>();
21             Reset();
22         }
23 
Read()24         public byte Read()
25         {
26             if(data.Count > 0)
27             {
28                 var result = data.Dequeue();
29                 NotifyParent();
30                 return result;
31             }
32             this.Log(LogLevel.Warning, "Attempted to read while no data in buffer. Returning 0.");
33             return 0;
34         }
35 
Write(byte value)36         public void Write(byte value)
37         {
38             if(lastCommand == Command.None)
39             {
40                 switch((Command)value)
41                 {
42                 case Command.Reset:
43                     AckAndReset();
44                     break;
45                 case Command.GetDeviceId:
46                     lock(data)
47                     {
48                         SendAck();
49                         data.Enqueue(0x00);
50                     }
51                     break;
52                 case Command.SetSampleRate:
53                 case Command.SetResolution:
54                     lastCommand = (Command)value;
55                     SendAck();
56                     break;
57                 default:
58                     this.Log(LogLevel.Warning, "Unhandled PS2 command: {0}", (Command)value);
59                     break;
60                 }
61             }
62             else
63             {
64                 switch(lastCommand)
65                 {
66                 case Command.SetSampleRate:
67                 case Command.SetResolution:
68                     SendAck();
69                     break;
70                 }
71                 lastCommand = Command.None;
72             }
73         }
74 
MoveBy(int x, int y)75         public void MoveBy(int x, int y)
76         {
77             byte dataByte = buttonState;
78             y = -y;
79             if(x < 0)
80             {
81                 dataByte |= 1 << 4;
82             }
83             if(y < 0)
84             {
85                 dataByte |= 1 << 5;
86             }
87 
88             x = x.Clamp(-255, 255);
89             y = y.Clamp(-255, 255);
90 
91             lock(data)
92             {
93                 data.Enqueue(dataByte);
94                 data.Enqueue((byte)x);
95                 data.Enqueue((byte)y);
96             }
97             NotifyParent();
98         }
99 
Press(MouseButton button = MouseButton.Left)100         public void Press(MouseButton button = MouseButton.Left)
101         {
102             buttonState |= (byte)button;
103             SendButtonState();
104         }
105 
Release(MouseButton button = MouseButton.Left)106         public void Release(MouseButton button = MouseButton.Left)
107         {
108             buttonState &= (byte) ~button;
109             SendButtonState();
110         }
111 
Reset()112         public void Reset()
113         {
114             buttonState = 0x08;
115             data.Clear();
116         }
117 
118         public IPS2Controller Controller { get; set; }
119 
SendButtonState()120         private void SendButtonState()
121         {
122             lock(data)
123             {
124                 data.Enqueue(buttonState);
125                 data.Enqueue(0x00);
126                 data.Enqueue(0x00);
127             }
128             NotifyParent();
129         }
130 
AckAndReset()131         private void AckAndReset()
132         {
133             Reset();
134             lock(data)
135             {
136                 SendAck();
137                 data.Enqueue((byte) Command.SelfTestPassed);
138                 data.Enqueue(0x00);
139             }
140         }
141 
SendAck()142         private void SendAck()
143         {
144             data.Enqueue((byte)Command.Acknowledge);
145             NotifyParent();
146         }
147 
NotifyParent()148         private void NotifyParent()
149         {
150             if(Controller != null)
151             {
152                 if(data.Count > 0)
153                 {
154                     Controller.Notify();
155                 }
156             }
157             else
158             {
159                 this.Log(LogLevel.Noisy, "PS2 device not connected to any controller issued an update.");
160             }
161         }
162 
163         private Command lastCommand;
164         private byte buttonState;
165         private readonly Queue<byte> data;
166 
167         enum Command : byte
168         {
169             Reset = 0xFF,
170             SetSampleRate = 0xF3,
171             GetDeviceId = 0xF2,
172             SetResolution = 0xE8,
173             Acknowledge = 0xFA,
174             SelfTestPassed = 0xAA,
175             None = 0x00,
176         }
177     }
178 }
179