1/* 2 * Copyright (c) 2008 The Regents of The University of Michigan 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 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Authors: Gabe Black 29 */ 30 31#include "dev/x86/i8042.hh" 32 33#include "base/bitunion.hh" 34#include "debug/I8042.hh" 35#include "mem/packet.hh" 36#include "mem/packet_access.hh" 37 38/** 39 * Note: For details on the implementation see 40 * https://wiki.osdev.org/%228042%22_PS/2_Controller 41 */ 42 43// The 8042 has a whopping 32 bytes of internal RAM. 44const uint8_t RamSize = 32; 45const uint8_t NumOutputBits = 14; 46 47 48X86ISA::I8042::I8042(Params *p) 49 : BasicPioDevice(p, 0), // pioSize arg is dummy value... not used 50 latency(p->pio_latency), 51 dataPort(p->data_port), commandPort(p->command_port), 52 statusReg(0), commandByte(0), dataReg(0), lastCommand(NoCommand), 53 mouse(p->mouse), keyboard(p->keyboard) 54{ 55 fatal_if(!mouse, "The i8042 model requires a mouse instance"); 56 fatal_if(!keyboard, "The i8042 model requires a keyboard instance"); 57 58 statusReg.passedSelfTest = 1; 59 statusReg.commandLast = 1; 60 statusReg.keyboardUnlocked = 1; 61 62 commandByte.convertScanCodes = 1; 63 commandByte.passedSelfTest = 1; 64 commandByte.keyboardFullInt = 1; 65 66 for (int i = 0; i < p->port_keyboard_int_pin_connection_count; i++) { 67 keyboardIntPin.push_back(new IntSourcePin<I8042>( 68 csprintf("%s.keyboard_int_pin[%d]", name(), i), i, this)); 69 } 70 for (int i = 0; i < p->port_mouse_int_pin_connection_count; i++) { 71 mouseIntPin.push_back(new IntSourcePin<I8042>( 72 csprintf("%s.mouse_int_pin[%d]", name(), i), i, this)); 73 } 74} 75 76 77AddrRangeList 78X86ISA::I8042::getAddrRanges() const 79{ 80 AddrRangeList ranges; 81 // TODO: Are these really supposed to be a single byte and not 4? 82 ranges.push_back(RangeSize(dataPort, 1)); 83 ranges.push_back(RangeSize(commandPort, 1)); 84 return ranges; 85} 86 87void 88X86ISA::I8042::writeData(uint8_t newData, bool mouse) 89{ 90 DPRINTF(I8042, "Set data %#02x.\n", newData); 91 dataReg = newData; 92 statusReg.outputFull = 1; 93 statusReg.mouseOutputFull = (mouse ? 1 : 0); 94 if (!mouse && commandByte.keyboardFullInt) { 95 DPRINTF(I8042, "Sending keyboard interrupt.\n"); 96 for (auto *wire: keyboardIntPin) { 97 wire->raise(); 98 //This is a hack 99 wire->lower(); 100 } 101 } else if (mouse && commandByte.mouseFullInt) { 102 DPRINTF(I8042, "Sending mouse interrupt.\n"); 103 for (auto *wire: mouseIntPin) { 104 wire->raise(); 105 //This is a hack 106 wire->lower(); 107 } 108 } 109} 110 111uint8_t 112X86ISA::I8042::readDataOut() 113{ 114 uint8_t data = dataReg; 115 statusReg.outputFull = 0; 116 statusReg.mouseOutputFull = 0; 117 if (keyboard->hostDataAvailable()) { 118 writeData(keyboard->hostRead(), false); 119 } else if (mouse->hostDataAvailable()) { 120 writeData(mouse->hostRead(), true); 121 } 122 return data; 123} 124 125Tick 126X86ISA::I8042::read(PacketPtr pkt) 127{ 128 assert(pkt->getSize() == 1); 129 Addr addr = pkt->getAddr(); 130 if (addr == dataPort) { 131 uint8_t data = readDataOut(); 132 //DPRINTF(I8042, "Read from data port got %#02x.\n", data); 133 pkt->setLE<uint8_t>(data); 134 } else if (addr == commandPort) { 135 //DPRINTF(I8042, "Read status as %#02x.\n", (uint8_t)statusReg); 136 pkt->setLE<uint8_t>((uint8_t)statusReg); 137 } else { 138 panic("Read from unrecognized port %#x.\n", addr); 139 } 140 pkt->makeAtomicResponse(); 141 return latency; 142} 143 144Tick 145X86ISA::I8042::write(PacketPtr pkt) 146{ 147 assert(pkt->getSize() == 1); 148 Addr addr = pkt->getAddr(); 149 uint8_t data = pkt->getLE<uint8_t>(); 150 if (addr == dataPort) { 151 statusReg.commandLast = 0; 152 switch (lastCommand) { 153 case NoCommand: 154 keyboard->hostWrite(data); 155 if (keyboard->hostDataAvailable()) 156 writeData(keyboard->hostRead(), false); 157 break; 158 case WriteToMouse: 159 mouse->hostWrite(data); 160 if (mouse->hostDataAvailable()) 161 writeData(mouse->hostRead(), true); 162 break; 163 case WriteCommandByte: 164 commandByte = data; 165 DPRINTF(I8042, "Got data %#02x for \"Write " 166 "command byte\" command.\n", data); 167 statusReg.passedSelfTest = (uint8_t)commandByte.passedSelfTest; 168 break; 169 case WriteMouseOutputBuff: 170 DPRINTF(I8042, "Got data %#02x for \"Write " 171 "mouse output buffer\" command.\n", data); 172 writeData(data, true); 173 break; 174 case WriteKeyboardOutputBuff: 175 DPRINTF(I8042, "Got data %#02x for \"Write " 176 "keyboad output buffer\" command.\n", data); 177 writeData(data, false); 178 break; 179 case WriteOutputPort: 180 DPRINTF(I8042, "Got data %#02x for \"Write " 181 "output port\" command.\n", data); 182 panic_if(bits(data, 0) != 1, "Reset bit should be 1"); 183 // Safe to ignore otherwise 184 break; 185 default: 186 panic("Data written for unrecognized " 187 "command %#02x\n", lastCommand); 188 } 189 lastCommand = NoCommand; 190 } else if (addr == commandPort) { 191 DPRINTF(I8042, "Got command %#02x.\n", data); 192 statusReg.commandLast = 1; 193 // These purposefully leave off the first byte of the controller RAM 194 // so it can be handled specially. 195 if (data > ReadControllerRamBase && 196 data < ReadControllerRamBase + RamSize) { 197 panic("Attempted to use i8042 read controller RAM command to " 198 "get byte %d.\n", data - ReadControllerRamBase); 199 } else if (data > WriteControllerRamBase && 200 data < WriteControllerRamBase + RamSize) { 201 panic("Attempted to use i8042 read controller RAM command to " 202 "get byte %d.\n", data - ReadControllerRamBase); 203 } else if (data >= PulseOutputBitBase && 204 data < PulseOutputBitBase + NumOutputBits) { 205 panic("Attempted to use i8042 pulse output bit command to " 206 "to pulse bit %d.\n", data - PulseOutputBitBase); 207 } 208 switch (data) { 209 case GetCommandByte: 210 DPRINTF(I8042, "Getting command byte.\n"); 211 writeData(commandByte); 212 break; 213 case WriteCommandByte: 214 DPRINTF(I8042, "Setting command byte.\n"); 215 lastCommand = WriteCommandByte; 216 break; 217 case CheckForPassword: 218 panic("i8042 \"Check for password\" command not implemented.\n"); 219 case LoadPassword: 220 panic("i8042 \"Load password\" command not implemented.\n"); 221 case CheckPassword: 222 panic("i8042 \"Check password\" command not implemented.\n"); 223 case DisableMouse: 224 DPRINTF(I8042, "Disabling mouse at controller.\n"); 225 commandByte.disableMouse = 1; 226 break; 227 case EnableMouse: 228 DPRINTF(I8042, "Enabling mouse at controller.\n"); 229 commandByte.disableMouse = 0; 230 break; 231 case TestMouse: 232 panic("i8042 \"Test mouse\" command not implemented.\n"); 233 case SelfTest: 234 panic("i8042 \"Self test\" command not implemented.\n"); 235 case InterfaceTest: 236 panic("i8042 \"Interface test\" command not implemented.\n"); 237 case DiagnosticDump: 238 panic("i8042 \"Diagnostic dump\" command not implemented.\n"); 239 case DisableKeyboard: 240 DPRINTF(I8042, "Disabling keyboard at controller.\n"); 241 commandByte.disableKeyboard = 1; 242 break; 243 case EnableKeyboard: 244 DPRINTF(I8042, "Enabling keyboard at controller.\n"); 245 commandByte.disableKeyboard = 0; 246 break; 247 case ReadInputPort: 248 panic("i8042 \"Read input port\" command not implemented.\n"); 249 case ContinuousPollLow: 250 panic("i8042 \"Continuous poll low\" command not implemented.\n"); 251 case ContinuousPollHigh: 252 panic("i8042 \"Continuous poll high\" command not implemented.\n"); 253 case ReadOutputPort: 254 panic("i8042 \"Read output port\" command not implemented.\n"); 255 case WriteOutputPort: 256 lastCommand = WriteOutputPort; 257 break; 258 case WriteKeyboardOutputBuff: 259 lastCommand = WriteKeyboardOutputBuff; 260 break; 261 case WriteMouseOutputBuff: 262 DPRINTF(I8042, "Got command to write to mouse output buffer.\n"); 263 lastCommand = WriteMouseOutputBuff; 264 break; 265 case WriteToMouse: 266 DPRINTF(I8042, "Expecting mouse command.\n"); 267 lastCommand = WriteToMouse; 268 break; 269 case DisableA20: 270 panic("i8042 \"Disable A20\" command not implemented.\n"); 271 case EnableA20: 272 panic("i8042 \"Enable A20\" command not implemented.\n"); 273 case ReadTestInputs: 274 panic("i8042 \"Read test inputs\" command not implemented.\n"); 275 case SystemReset: 276 panic("i8042 \"System reset\" command not implemented.\n"); 277 default: 278 warn("Write to unknown i8042 " 279 "(keyboard controller) command port.\n"); 280 } 281 } else { 282 panic("Write to unrecognized port %#x.\n", addr); 283 } 284 pkt->makeAtomicResponse(); 285 return latency; 286} 287 288void 289X86ISA::I8042::serialize(CheckpointOut &cp) const 290{ 291 SERIALIZE_SCALAR(dataPort); 292 SERIALIZE_SCALAR(commandPort); 293 SERIALIZE_SCALAR(statusReg); 294 SERIALIZE_SCALAR(commandByte); 295 SERIALIZE_SCALAR(dataReg); 296 SERIALIZE_SCALAR(lastCommand); 297} 298 299void 300X86ISA::I8042::unserialize(CheckpointIn &cp) 301{ 302 UNSERIALIZE_SCALAR(dataPort); 303 UNSERIALIZE_SCALAR(commandPort); 304 UNSERIALIZE_SCALAR(statusReg); 305 UNSERIALIZE_SCALAR(commandByte); 306 UNSERIALIZE_SCALAR(dataReg); 307 UNSERIALIZE_SCALAR(lastCommand); 308} 309 310X86ISA::I8042 * 311I8042Params::create() 312{ 313 return new X86ISA::I8042(this); 314} 315