Serenity Operating System
at hosted 326 lines 9.0 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 <AK/Memory.h> 28#include <Kernel/Devices/PS2MouseDevice.h> 29#include <Kernel/Devices/VMWareBackdoor.h> 30#include <LibBareMetal/IO.h> 31 32namespace Kernel { 33 34#define IRQ_MOUSE 12 35#define I8042_BUFFER 0x60 36#define I8042_STATUS 0x64 37#define I8042_ACK 0xFA 38#define I8042_BUFFER_FULL 0x01 39#define I8042_WHICH_BUFFER 0x20 40#define I8042_MOUSE_BUFFER 0x20 41#define I8042_KEYBOARD_BUFFER 0x00 42 43#define PS2MOUSE_SET_RESOLUTION 0xE8 44#define PS2MOUSE_STATUS_REQUEST 0xE9 45#define PS2MOUSE_REQUEST_SINGLE_PACKET 0xEB 46#define PS2MOUSE_GET_DEVICE_ID 0xF2 47#define PS2MOUSE_SET_SAMPLE_RATE 0xF3 48#define PS2MOUSE_ENABLE_PACKET_STREAMING 0xF4 49#define PS2MOUSE_DISABLE_PACKET_STREAMING 0xF5 50#define PS2MOUSE_SET_DEFAULTS 0xF6 51#define PS2MOUSE_RESEND 0xFE 52#define PS2MOUSE_RESET 0xFF 53 54#define PS2MOUSE_INTELLIMOUSE_ID 0x03 55 56//#define PS2MOUSE_DEBUG 57 58static PS2MouseDevice* s_the; 59 60PS2MouseDevice::PS2MouseDevice() 61 : IRQHandler(IRQ_MOUSE) 62 , CharacterDevice(10, 1) 63{ 64 s_the = this; 65 initialize(); 66} 67 68PS2MouseDevice::~PS2MouseDevice() 69{ 70} 71 72PS2MouseDevice& PS2MouseDevice::the() 73{ 74 return *s_the; 75} 76 77void PS2MouseDevice::handle_irq(const RegisterState&) 78{ 79 if (auto* backdoor = VMWareBackdoor::the()) { 80 if (backdoor->vmmouse_is_absolute()) { 81 IO::in8(I8042_BUFFER); 82 auto packet = backdoor->receive_mouse_packet(); 83 if (packet.has_value()) 84 m_queue.enqueue(packet.value()); 85 return; 86 } 87 } 88 for (;;) { 89 u8 status = IO::in8(I8042_STATUS); 90 if (!(((status & I8042_WHICH_BUFFER) == I8042_MOUSE_BUFFER) && (status & I8042_BUFFER_FULL))) 91 return; 92 93 u8 data = IO::in8(I8042_BUFFER); 94 m_data[m_data_state] = data; 95 96 auto commit_packet = [&] { 97 m_data_state = 0; 98#ifdef PS2MOUSE_DEBUG 99 dbg() << "PS2Mouse: " << m_data[1] << ", " << m_data[2] << " " << ((m_data[0] & 1) ? "Left" : "") << " " << ((m_data[0] & 2) ? "Right" : "") << " (buffered: " << m_queue.size() << ")"; 100#endif 101 parse_data_packet(); 102 }; 103 104 switch (m_data_state) { 105 case 0: 106 if (!(data & 0x08)) { 107 dbg() << "PS2Mouse: Stream out of sync."; 108 break; 109 } 110 ++m_data_state; 111 break; 112 case 1: 113 ++m_data_state; 114 break; 115 case 2: 116 if (m_has_wheel) { 117 ++m_data_state; 118 break; 119 } 120 commit_packet(); 121 break; 122 case 3: 123 ASSERT(m_has_wheel); 124 commit_packet(); 125 break; 126 } 127 } 128} 129 130void PS2MouseDevice::parse_data_packet() 131{ 132 int x = m_data[1]; 133 int y = m_data[2]; 134 int z = 0; 135 if (m_has_wheel) 136 z = (char)m_data[3]; 137 bool x_overflow = m_data[0] & 0x40; 138 bool y_overflow = m_data[0] & 0x80; 139 bool x_sign = m_data[0] & 0x10; 140 bool y_sign = m_data[0] & 0x20; 141 if (x && x_sign) 142 x -= 0x100; 143 if (y && y_sign) 144 y -= 0x100; 145 if (x_overflow || y_overflow) { 146 x = 0; 147 y = 0; 148 } 149 MousePacket packet; 150 packet.x = x; 151 packet.y = y; 152 packet.z = z; 153 packet.buttons = m_data[0] & 0x07; 154 packet.is_relative = true; 155#ifdef PS2MOUSE_DEBUG 156 dbg() << "PS2 Relative Mouse: Buttons " << String::format("%x", packet.buttons); 157 dbg() << "Mouse: X " << packet.x << ", Y " << packet.y << ", Z " << packet.z; 158#endif 159 m_queue.enqueue(packet); 160} 161 162void PS2MouseDevice::wait_then_write(u8 port, u8 data) 163{ 164 prepare_for_output(); 165 IO::out8(port, data); 166} 167 168u8 PS2MouseDevice::wait_then_read(u8 port) 169{ 170 prepare_for_input(); 171 return IO::in8(port); 172} 173 174void PS2MouseDevice::initialize() 175{ 176 // Enable PS aux port 177 wait_then_write(I8042_STATUS, 0xa8); 178 179 check_device_presence(); 180 181 if (m_device_present) 182 initialize_device(); 183} 184 185void PS2MouseDevice::check_device_presence() 186{ 187 mouse_write(PS2MOUSE_REQUEST_SINGLE_PACKET); 188 u8 maybe_ack = mouse_read(); 189 if (maybe_ack == I8042_ACK) { 190 m_device_present = true; 191 klog() << "PS2MouseDevice: Device detected"; 192 193 // the mouse will send a packet of data, since that's what we asked 194 // for. we don't care about the content. 195 mouse_read(); 196 mouse_read(); 197 mouse_read(); 198 } else { 199 m_device_present = false; 200 klog() << "PS2MouseDevice: Device not detected"; 201 } 202} 203 204void PS2MouseDevice::initialize_device() 205{ 206 if (!m_device_present) 207 return; 208 209 // Enable interrupts 210 wait_then_write(I8042_STATUS, 0x20); 211 212 // Enable the PS/2 mouse IRQ (12). 213 // NOTE: The keyboard uses IRQ 1 (and is enabled by bit 0 in this register). 214 u8 status = wait_then_read(I8042_BUFFER) | 2; 215 wait_then_write(I8042_STATUS, 0x60); 216 wait_then_write(I8042_BUFFER, status); 217 218 // Set default settings. 219 mouse_write(PS2MOUSE_SET_DEFAULTS); 220 expect_ack(); 221 222 // Enable. 223 mouse_write(PS2MOUSE_ENABLE_PACKET_STREAMING); 224 expect_ack(); 225 226 mouse_write(PS2MOUSE_GET_DEVICE_ID); 227 expect_ack(); 228 u8 device_id = mouse_read(); 229 230 if (device_id != PS2MOUSE_INTELLIMOUSE_ID) { 231 // Send magical wheel initiation sequence. 232 mouse_write(PS2MOUSE_SET_SAMPLE_RATE); 233 expect_ack(); 234 mouse_write(200); 235 expect_ack(); 236 mouse_write(PS2MOUSE_SET_SAMPLE_RATE); 237 expect_ack(); 238 mouse_write(100); 239 expect_ack(); 240 mouse_write(PS2MOUSE_SET_SAMPLE_RATE); 241 expect_ack(); 242 mouse_write(80); 243 expect_ack(); 244 245 mouse_write(PS2MOUSE_GET_DEVICE_ID); 246 expect_ack(); 247 device_id = mouse_read(); 248 } 249 250 if (device_id == PS2MOUSE_INTELLIMOUSE_ID) { 251 m_has_wheel = true; 252 klog() << "PS2MouseDevice: Mouse wheel enabled!"; 253 } else { 254 klog() << "PS2MouseDevice: No mouse wheel detected!"; 255 } 256 257 enable_irq(); 258} 259 260void PS2MouseDevice::expect_ack() 261{ 262 u8 data = mouse_read(); 263 ASSERT(data == I8042_ACK); 264} 265 266void PS2MouseDevice::prepare_for_input() 267{ 268 for (;;) { 269 if (IO::in8(I8042_STATUS) & 1) 270 return; 271 } 272} 273 274void PS2MouseDevice::prepare_for_output() 275{ 276 for (;;) { 277 if (!(IO::in8(I8042_STATUS) & 2)) 278 return; 279 } 280} 281 282void PS2MouseDevice::mouse_write(u8 data) 283{ 284 prepare_for_output(); 285 IO::out8(I8042_STATUS, 0xd4); 286 prepare_for_output(); 287 IO::out8(I8042_BUFFER, data); 288} 289 290u8 PS2MouseDevice::mouse_read() 291{ 292 prepare_for_input(); 293 return IO::in8(I8042_BUFFER); 294} 295 296bool PS2MouseDevice::can_read(const FileDescription&) const 297{ 298 return !m_queue.is_empty(); 299} 300 301ssize_t PS2MouseDevice::read(FileDescription&, u8* buffer, ssize_t size) 302{ 303 ASSERT(size > 0); 304 size_t nread = 0; 305 size_t remaining_space_in_buffer = static_cast<size_t>(size) - nread; 306 while (!m_queue.is_empty() && remaining_space_in_buffer) { 307 auto packet = m_queue.dequeue(); 308#ifdef PS2MOUSE_DEBUG 309 dbg() << "PS2 Mouse Read: Buttons " << String::format("%x", packet.buttons); 310 dbg() << "PS2 Mouse: X " << packet.x << ", Y " << packet.y << ", Z " << packet.z << " Relative " << packet.buttons; 311 dbg() << "PS2 Mouse Read: Filter packets"; 312#endif 313 size_t bytes_read_from_packet = min(remaining_space_in_buffer, sizeof(MousePacket)); 314 memcpy(buffer + nread, &packet, bytes_read_from_packet); 315 nread += bytes_read_from_packet; 316 remaining_space_in_buffer -= bytes_read_from_packet; 317 } 318 return nread; 319} 320 321ssize_t PS2MouseDevice::write(FileDescription&, const u8*, ssize_t) 322{ 323 return 0; 324} 325 326}