Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <Kernel/Devices/PS2MouseDevice.h>
28#include <Kernel/Devices/VMWareBackdoor.h>
29#include <LibBareMetal/IO.h>
30
31namespace Kernel {
32
33#define IRQ_MOUSE 12
34#define I8042_BUFFER 0x60
35#define I8042_STATUS 0x64
36#define I8042_ACK 0xFA
37#define I8042_BUFFER_FULL 0x01
38#define I8042_WHICH_BUFFER 0x20
39#define I8042_MOUSE_BUFFER 0x20
40#define I8042_KEYBOARD_BUFFER 0x00
41
42#define PS2MOUSE_SET_RESOLUTION 0xE8
43#define PS2MOUSE_STATUS_REQUEST 0xE9
44#define PS2MOUSE_REQUEST_SINGLE_PACKET 0xEB
45#define PS2MOUSE_GET_DEVICE_ID 0xF2
46#define PS2MOUSE_SET_SAMPLE_RATE 0xF3
47#define PS2MOUSE_ENABLE_PACKET_STREAMING 0xF4
48#define PS2MOUSE_DISABLE_PACKET_STREAMING 0xF5
49#define PS2MOUSE_SET_DEFAULTS 0xF6
50#define PS2MOUSE_RESEND 0xFE
51#define PS2MOUSE_RESET 0xFF
52
53#define PS2MOUSE_LEFT_CLICK 0x01
54#define PS2MOUSE_RIGHT_CLICK 0x02
55#define PS2MOUSE_MIDDLE_CLICK 0x04
56
57#define VMMOUSE_LEFT_CLICK 0x20
58#define VMMOUSE_RIGHT_CLICK 0x10
59#define VMMOUSE_MIDDLE_CLICK 0x08
60
61#define PS2MOUSE_INTELLIMOUSE_ID 0x03
62
63//#define PS2MOUSE_DEBUG
64
65static PS2MouseDevice* s_the;
66
67PS2MouseDevice::PS2MouseDevice()
68 : IRQHandler(IRQ_MOUSE)
69 , CharacterDevice(10, 1)
70{
71 s_the = this;
72 initialize();
73}
74
75PS2MouseDevice::~PS2MouseDevice()
76{
77}
78
79PS2MouseDevice& PS2MouseDevice::the()
80{
81 return *s_the;
82}
83
84void PS2MouseDevice::handle_vmmouse_absolute_pointer()
85{
86 VMWareCommand command;
87 command.bx = 0;
88 command.command = VMMOUSE_STATUS;
89 VMWareBackdoor::the().send(command);
90 if (command.ax == 0xFFFF0000) {
91#ifdef PS2MOUSE_DEBUG
92 dbgprintf("Reset vmmouse.\n");
93#endif
94 VMWareBackdoor::the().disable_absolute_vmmouse();
95 VMWareBackdoor::the().enable_absolute_vmmouse();
96 return;
97 }
98 int words = command.ax & 0xFFFF;
99
100 if (!words || words % 4)
101 return;
102 command.size = 4;
103 command.command = VMMOUSE_DATA;
104 VMWareBackdoor::the().send(command);
105
106 int buttons = (command.ax & 0xFFFF);
107 int x = (command.bx);
108 int y = (command.cx);
109 int z = (command.dx);
110
111#ifdef PS2MOUSE_DEBUG
112 dbgprintf("Absolute Mouse: Buttons %x\n", buttons);
113 dbgprintf("Mouse: X %d, Y %d, Z %d\n", x, y, z);
114#endif
115 MousePacket packet;
116 packet.x = x;
117 packet.y = y;
118 packet.z = z;
119 if (buttons & VMMOUSE_LEFT_CLICK) {
120 packet.buttons |= PS2MOUSE_LEFT_CLICK;
121 }
122 if (buttons & VMMOUSE_RIGHT_CLICK) {
123 packet.buttons |= PS2MOUSE_RIGHT_CLICK;
124 }
125 if (buttons & VMMOUSE_MIDDLE_CLICK) {
126 packet.buttons |= PS2MOUSE_MIDDLE_CLICK;
127 }
128 packet.is_relative = false;
129 m_queue.enqueue(packet);
130}
131
132void PS2MouseDevice::handle_irq(RegisterState&)
133{
134
135 if (VMWareBackdoor::the().vmmouse_is_absolute()) {
136#ifdef PS2MOUSE_DEBUG
137 dbgprintf("Handling PS2 vmmouse.\n");
138#endif
139 IO::in8(I8042_BUFFER);
140 handle_vmmouse_absolute_pointer();
141 return;
142 }
143#ifdef PS2MOUSE_DEBUG
144 dbgprintf("Handling classical PS2 mouse.\n");
145#endif
146 for (;;) {
147 u8 status = IO::in8(I8042_STATUS);
148 if (!(((status & I8042_WHICH_BUFFER) == I8042_MOUSE_BUFFER) && (status & I8042_BUFFER_FULL)))
149 return;
150
151 u8 data = IO::in8(I8042_BUFFER);
152 m_data[m_data_state] = data;
153
154 auto commit_packet = [&] {
155 m_data_state = 0;
156#ifdef PS2MOUSE_DEBUG
157 dbgprintf("PS2Mouse: %d, %d %s %s (buffered: %u)\n",
158 m_data[1],
159 m_data[2],
160 (m_data[0] & 1) ? "Left" : "",
161 (m_data[0] & 2) ? "Right" : "",
162 m_queue.size());
163#endif
164 parse_data_packet();
165 };
166
167 switch (m_data_state) {
168 case 0:
169 if (!(data & 0x08)) {
170 dbgprintf("PS2Mouse: Stream out of sync.\n");
171 break;
172 }
173 ++m_data_state;
174 break;
175 case 1:
176 ++m_data_state;
177 break;
178 case 2:
179 if (m_has_wheel) {
180 ++m_data_state;
181 break;
182 }
183 commit_packet();
184 break;
185 case 3:
186 ASSERT(m_has_wheel);
187 commit_packet();
188 break;
189 }
190 }
191}
192
193void PS2MouseDevice::parse_data_packet()
194{
195 int x = m_data[1];
196 int y = m_data[2];
197 int z = 0;
198 if (m_has_wheel)
199 z = (char)m_data[3];
200 bool x_overflow = m_data[0] & 0x40;
201 bool y_overflow = m_data[0] & 0x80;
202 bool x_sign = m_data[0] & 0x10;
203 bool y_sign = m_data[0] & 0x20;
204 if (x && x_sign)
205 x -= 0x100;
206 if (y && y_sign)
207 y -= 0x100;
208 if (x_overflow || y_overflow) {
209 x = 0;
210 y = 0;
211 }
212 MousePacket packet;
213 packet.x = x;
214 packet.y = y;
215 packet.z = z;
216 packet.buttons = m_data[0] & 0x07;
217 packet.is_relative = true;
218#ifdef PS2MOUSE_DEBUG
219 dbgprintf("PS2 Relative Mouse: Buttons %x\n", packet.buttons);
220 dbgprintf("Mouse: X %d, Y %d, Z %d\n", packet.x, packet.y, packet.z);
221#endif
222 m_queue.enqueue(packet);
223}
224
225void PS2MouseDevice::wait_then_write(u8 port, u8 data)
226{
227 prepare_for_output();
228 IO::out8(port, data);
229}
230
231u8 PS2MouseDevice::wait_then_read(u8 port)
232{
233 prepare_for_input();
234 return IO::in8(port);
235}
236
237void PS2MouseDevice::initialize()
238{
239 // Enable PS aux port
240 wait_then_write(I8042_STATUS, 0xa8);
241
242 check_device_presence();
243
244 if (m_device_present)
245 initialize_device();
246}
247
248void PS2MouseDevice::check_device_presence()
249{
250 mouse_write(PS2MOUSE_REQUEST_SINGLE_PACKET);
251 u8 maybe_ack = mouse_read();
252 if (maybe_ack == I8042_ACK) {
253 m_device_present = true;
254 kprintf("PS2MouseDevice: Device detected\n");
255
256 // the mouse will send a packet of data, since that's what we asked
257 // for. we don't care about the content.
258 mouse_read();
259 mouse_read();
260 mouse_read();
261 } else {
262 m_device_present = false;
263 kprintf("PS2MouseDevice: Device not detected\n");
264 }
265}
266
267void PS2MouseDevice::initialize_device()
268{
269 if (!m_device_present)
270 return;
271
272 // Enable interrupts
273 wait_then_write(I8042_STATUS, 0x20);
274
275 // Enable the PS/2 mouse IRQ (12).
276 // NOTE: The keyboard uses IRQ 1 (and is enabled by bit 0 in this register).
277 u8 status = wait_then_read(I8042_BUFFER) | 2;
278 wait_then_write(I8042_STATUS, 0x60);
279 wait_then_write(I8042_BUFFER, status);
280
281 // Set default settings.
282 mouse_write(PS2MOUSE_SET_DEFAULTS);
283 expect_ack();
284
285 // Enable.
286 mouse_write(PS2MOUSE_ENABLE_PACKET_STREAMING);
287 expect_ack();
288
289 mouse_write(PS2MOUSE_GET_DEVICE_ID);
290 expect_ack();
291 u8 device_id = mouse_read();
292
293 if (device_id != PS2MOUSE_INTELLIMOUSE_ID) {
294 // Send magical wheel initiation sequence.
295 mouse_write(PS2MOUSE_SET_SAMPLE_RATE);
296 expect_ack();
297 mouse_write(200);
298 expect_ack();
299 mouse_write(PS2MOUSE_SET_SAMPLE_RATE);
300 expect_ack();
301 mouse_write(100);
302 expect_ack();
303 mouse_write(PS2MOUSE_SET_SAMPLE_RATE);
304 expect_ack();
305 mouse_write(80);
306 expect_ack();
307
308 mouse_write(PS2MOUSE_GET_DEVICE_ID);
309 expect_ack();
310 device_id = mouse_read();
311 }
312
313 if (device_id == PS2MOUSE_INTELLIMOUSE_ID) {
314 m_has_wheel = true;
315 kprintf("PS2MouseDevice: Mouse wheel enabled!\n");
316 } else {
317 kprintf("PS2MouseDevice: No mouse wheel detected!\n");
318 }
319
320 enable_irq();
321}
322
323void PS2MouseDevice::expect_ack()
324{
325 u8 data = mouse_read();
326 ASSERT(data == I8042_ACK);
327}
328
329void PS2MouseDevice::prepare_for_input()
330{
331 for (;;) {
332 if (IO::in8(I8042_STATUS) & 1)
333 return;
334 }
335}
336
337void PS2MouseDevice::prepare_for_output()
338{
339 for (;;) {
340 if (!(IO::in8(I8042_STATUS) & 2))
341 return;
342 }
343}
344
345void PS2MouseDevice::mouse_write(u8 data)
346{
347 prepare_for_output();
348 IO::out8(I8042_STATUS, 0xd4);
349 prepare_for_output();
350 IO::out8(I8042_BUFFER, data);
351}
352
353u8 PS2MouseDevice::mouse_read()
354{
355 prepare_for_input();
356 return IO::in8(I8042_BUFFER);
357}
358
359bool PS2MouseDevice::can_read(const FileDescription&) const
360{
361 return !m_queue.is_empty();
362}
363
364ssize_t PS2MouseDevice::read(FileDescription&, u8* buffer, ssize_t size)
365{
366 ASSERT(size > 0);
367 size_t nread = 0;
368 size_t remaining_space_in_buffer = static_cast<size_t>(size) - nread;
369 while (!m_queue.is_empty() && remaining_space_in_buffer) {
370 auto packet = m_queue.dequeue();
371#ifdef PS2MOUSE_DEBUG
372 dbgprintf("PS2 Mouse Read: Buttons %x\n", packet.buttons);
373 dbgprintf("PS2 Mouse Read: X %d, Y %d, Z %d, Relative %d\n", packet.x, packet.y, packet.z, packet.buttons);
374 dbgprintf("PS2 Mouse Read: Filter packets\n");
375#endif
376 size_t bytes_read_from_packet = min(remaining_space_in_buffer, sizeof(MousePacket));
377 memcpy(buffer + nread, &packet, bytes_read_from_packet);
378 nread += bytes_read_from_packet;
379 remaining_space_in_buffer -= bytes_read_from_packet;
380 }
381 return nread;
382}
383
384ssize_t PS2MouseDevice::write(FileDescription&, const u8*, ssize_t)
385{
386 return 0;
387}
388
389}