Serenity Operating System
at portability 389 lines 10 kB view raw
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}