i8042.cc revision 9808:13ffc0066b76
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 "base/bitunion.hh" 32#include "debug/I8042.hh" 33#include "dev/x86/i8042.hh" 34#include "mem/packet.hh" 35#include "mem/packet_access.hh" 36 37// The 8042 has a whopping 32 bytes of internal RAM. 38const uint8_t RamSize = 32; 39const uint8_t NumOutputBits = 14; 40const uint8_t X86ISA::PS2Keyboard::ID[] = {0xab, 0x83}; 41const uint8_t X86ISA::PS2Mouse::ID[] = {0x00}; 42const uint8_t CommandAck = 0xfa; 43const uint8_t CommandNack = 0xfe; 44const uint8_t BatSuccessful = 0xaa; 45 46 47X86ISA::I8042::I8042(Params *p) 48 : BasicPioDevice(p, 0), // pioSize arg is dummy value... not used 49 latency(p->pio_latency), 50 dataPort(p->data_port), commandPort(p->command_port), 51 statusReg(0), commandByte(0), dataReg(0), lastCommand(NoCommand), 52 mouseIntPin(p->mouse_int_pin), keyboardIntPin(p->keyboard_int_pin) 53{ 54 statusReg.passedSelfTest = 1; 55 statusReg.commandLast = 1; 56 statusReg.keyboardUnlocked = 1; 57 58 commandByte.convertScanCodes = 1; 59 commandByte.passedSelfTest = 1; 60 commandByte.keyboardFullInt = 1; 61} 62 63 64AddrRangeList 65X86ISA::I8042::getAddrRanges() const 66{ 67 AddrRangeList ranges; 68 // TODO: Are these really supposed to be a single byte and not 4? 69 ranges.push_back(RangeSize(dataPort, 1)); 70 ranges.push_back(RangeSize(commandPort, 1)); 71 return ranges; 72} 73 74void 75X86ISA::I8042::writeData(uint8_t newData, bool mouse) 76{ 77 DPRINTF(I8042, "Set data %#02x.\n", newData); 78 dataReg = newData; 79 statusReg.outputFull = 1; 80 statusReg.mouseOutputFull = (mouse ? 1 : 0); 81 if (!mouse && commandByte.keyboardFullInt) { 82 DPRINTF(I8042, "Sending keyboard interrupt.\n"); 83 keyboardIntPin->raise(); 84 //This is a hack 85 keyboardIntPin->lower(); 86 } else if (mouse && commandByte.mouseFullInt) { 87 DPRINTF(I8042, "Sending mouse interrupt.\n"); 88 mouseIntPin->raise(); 89 //This is a hack 90 mouseIntPin->lower(); 91 } 92} 93 94void 95X86ISA::PS2Device::ack() 96{ 97 bufferData(&CommandAck, sizeof(CommandAck)); 98} 99 100void 101X86ISA::PS2Device::nack() 102{ 103 bufferData(&CommandNack, sizeof(CommandNack)); 104} 105 106void 107X86ISA::PS2Device::bufferData(const uint8_t *data, int size) 108{ 109 assert(data || size == 0); 110 while (size) { 111 outBuffer.push(*(data++)); 112 size--; 113 } 114} 115 116uint8_t 117X86ISA::I8042::readDataOut() 118{ 119 uint8_t data = dataReg; 120 statusReg.outputFull = 0; 121 statusReg.mouseOutputFull = 0; 122 if (keyboard.hasData()) { 123 writeData(keyboard.getData(), false); 124 } else if (mouse.hasData()) { 125 writeData(mouse.getData(), true); 126 } 127 return data; 128} 129 130bool 131X86ISA::PS2Keyboard::processData(uint8_t data) 132{ 133 if (lastCommand != NoCommand) { 134 switch (lastCommand) { 135 case LEDWrite: 136 DPRINTF(I8042, "Setting LEDs: " 137 "caps lock %s, num lock %s, scroll lock %s\n", 138 bits(data, 2) ? "on" : "off", 139 bits(data, 1) ? "on" : "off", 140 bits(data, 0) ? "on" : "off"); 141 ack(); 142 lastCommand = NoCommand; 143 break; 144 case TypematicInfo: 145 DPRINTF(I8042, "Setting typematic info to %#02x.\n", data); 146 ack(); 147 lastCommand = NoCommand; 148 break; 149 } 150 return hasData(); 151 } 152 switch (data) { 153 case LEDWrite: 154 DPRINTF(I8042, "Got LED write command.\n"); 155 ack(); 156 lastCommand = LEDWrite; 157 break; 158 case DiagnosticEcho: 159 panic("Keyboard diagnostic echo unimplemented.\n"); 160 case AlternateScanCodes: 161 panic("Accessing alternate scan codes unimplemented.\n"); 162 case ReadID: 163 DPRINTF(I8042, "Got keyboard read ID command.\n"); 164 ack(); 165 bufferData((uint8_t *)&ID, sizeof(ID)); 166 break; 167 case TypematicInfo: 168 DPRINTF(I8042, "Setting typematic info.\n"); 169 ack(); 170 lastCommand = TypematicInfo; 171 break; 172 case Enable: 173 DPRINTF(I8042, "Enabling the keyboard.\n"); 174 ack(); 175 break; 176 case Disable: 177 DPRINTF(I8042, "Disabling the keyboard.\n"); 178 ack(); 179 break; 180 case DefaultsAndDisable: 181 DPRINTF(I8042, "Disabling and resetting the keyboard.\n"); 182 ack(); 183 break; 184 case AllKeysToTypematic: 185 panic("Setting all keys to typemantic unimplemented.\n"); 186 case AllKeysToMakeRelease: 187 panic("Setting all keys to make/release unimplemented.\n"); 188 case AllKeysToMake: 189 panic("Setting all keys to make unimplemented.\n"); 190 case AllKeysToTypematicMakeRelease: 191 panic("Setting all keys to " 192 "typematic/make/release unimplemented.\n"); 193 case KeyToTypematic: 194 panic("Setting a key to typematic unimplemented.\n"); 195 case KeyToMakeRelease: 196 panic("Setting a key to make/release unimplemented.\n"); 197 case KeyToMakeOnly: 198 panic("Setting key to make only unimplemented.\n"); 199 case Resend: 200 panic("Keyboard resend unimplemented.\n"); 201 case Reset: 202 panic("Keyboard reset unimplemented.\n"); 203 default: 204 panic("Unknown keyboard command %#02x.\n", data); 205 } 206 return hasData(); 207} 208 209bool 210X86ISA::PS2Mouse::processData(uint8_t data) 211{ 212 if (lastCommand != NoCommand) { 213 switch(lastCommand) { 214 case SetResolution: 215 DPRINTF(I8042, "Mouse resolution set to %d.\n", data); 216 resolution = data; 217 ack(); 218 lastCommand = NoCommand; 219 break; 220 case SampleRate: 221 DPRINTF(I8042, "Mouse sample rate %d samples " 222 "per second.\n", data); 223 sampleRate = data; 224 ack(); 225 lastCommand = NoCommand; 226 break; 227 default: 228 panic("Not expecting data for a mouse command.\n"); 229 } 230 return hasData(); 231 } 232 switch (data) { 233 case Scale1to1: 234 DPRINTF(I8042, "Setting mouse scale to 1:1.\n"); 235 status.twoToOne = 0; 236 ack(); 237 break; 238 case Scale2to1: 239 DPRINTF(I8042, "Setting mouse scale to 2:1.\n"); 240 status.twoToOne = 1; 241 ack(); 242 break; 243 case SetResolution: 244 DPRINTF(I8042, "Setting mouse resolution.\n"); 245 lastCommand = SetResolution; 246 ack(); 247 break; 248 case GetStatus: 249 DPRINTF(I8042, "Getting mouse status.\n"); 250 ack(); 251 bufferData((uint8_t *)&(status), 1); 252 bufferData(&resolution, sizeof(resolution)); 253 bufferData(&sampleRate, sizeof(sampleRate)); 254 break; 255 case ReadData: 256 panic("Reading mouse data unimplemented.\n"); 257 case ResetWrapMode: 258 panic("Resetting mouse wrap mode unimplemented.\n"); 259 case WrapMode: 260 panic("Setting mouse wrap mode unimplemented.\n"); 261 case RemoteMode: 262 panic("Setting mouse remote mode unimplemented.\n"); 263 case ReadID: 264 DPRINTF(I8042, "Mouse ID requested.\n"); 265 ack(); 266 bufferData(ID, sizeof(ID)); 267 break; 268 case SampleRate: 269 DPRINTF(I8042, "Setting mouse sample rate.\n"); 270 lastCommand = SampleRate; 271 ack(); 272 break; 273 case DisableReporting: 274 DPRINTF(I8042, "Disabling data reporting.\n"); 275 status.enabled = 0; 276 ack(); 277 break; 278 case EnableReporting: 279 DPRINTF(I8042, "Enabling data reporting.\n"); 280 status.enabled = 1; 281 ack(); 282 break; 283 case DefaultsAndDisable: 284 DPRINTF(I8042, "Disabling and resetting mouse.\n"); 285 sampleRate = 100; 286 resolution = 4; 287 status.twoToOne = 0; 288 status.enabled = 0; 289 ack(); 290 break; 291 case Resend: 292 panic("Mouse resend unimplemented.\n"); 293 case Reset: 294 DPRINTF(I8042, "Resetting the mouse.\n"); 295 sampleRate = 100; 296 resolution = 4; 297 status.twoToOne = 0; 298 status.enabled = 0; 299 ack(); 300 bufferData(&BatSuccessful, sizeof(BatSuccessful)); 301 bufferData(ID, sizeof(ID)); 302 break; 303 default: 304 warn("Unknown mouse command %#02x.\n", data); 305 nack(); 306 break; 307 } 308 return hasData(); 309} 310 311 312Tick 313X86ISA::I8042::read(PacketPtr pkt) 314{ 315 assert(pkt->getSize() == 1); 316 Addr addr = pkt->getAddr(); 317 if (addr == dataPort) { 318 uint8_t data = readDataOut(); 319 //DPRINTF(I8042, "Read from data port got %#02x.\n", data); 320 pkt->set<uint8_t>(data); 321 } else if (addr == commandPort) { 322 //DPRINTF(I8042, "Read status as %#02x.\n", (uint8_t)statusReg); 323 pkt->set<uint8_t>((uint8_t)statusReg); 324 } else { 325 panic("Read from unrecognized port %#x.\n", addr); 326 } 327 pkt->makeAtomicResponse(); 328 return latency; 329} 330 331Tick 332X86ISA::I8042::write(PacketPtr pkt) 333{ 334 assert(pkt->getSize() == 1); 335 Addr addr = pkt->getAddr(); 336 uint8_t data = pkt->get<uint8_t>(); 337 if (addr == dataPort) { 338 statusReg.commandLast = 0; 339 switch (lastCommand) { 340 case NoCommand: 341 if (keyboard.processData(data)) { 342 writeData(keyboard.getData(), false); 343 } 344 break; 345 case WriteToMouse: 346 if (mouse.processData(data)) { 347 writeData(mouse.getData(), true); 348 } 349 break; 350 case WriteCommandByte: 351 commandByte = data; 352 DPRINTF(I8042, "Got data %#02x for \"Write " 353 "command byte\" command.\n", data); 354 statusReg.passedSelfTest = (uint8_t)commandByte.passedSelfTest; 355 break; 356 case WriteMouseOutputBuff: 357 DPRINTF(I8042, "Got data %#02x for \"Write " 358 "mouse output buffer\" command.\n", data); 359 writeData(data, true); 360 break; 361 default: 362 panic("Data written for unrecognized " 363 "command %#02x\n", lastCommand); 364 } 365 lastCommand = NoCommand; 366 } else if (addr == commandPort) { 367 DPRINTF(I8042, "Got command %#02x.\n", data); 368 statusReg.commandLast = 1; 369 // These purposefully leave off the first byte of the controller RAM 370 // so it can be handled specially. 371 if (data > ReadControllerRamBase && 372 data < ReadControllerRamBase + RamSize) { 373 panic("Attempted to use i8042 read controller RAM command to " 374 "get byte %d.\n", data - ReadControllerRamBase); 375 } else if (data > WriteControllerRamBase && 376 data < WriteControllerRamBase + RamSize) { 377 panic("Attempted to use i8042 read controller RAM command to " 378 "get byte %d.\n", data - ReadControllerRamBase); 379 } else if (data >= PulseOutputBitBase && 380 data < PulseOutputBitBase + NumOutputBits) { 381 panic("Attempted to use i8042 pulse output bit command to " 382 "to pulse bit %d.\n", data - PulseOutputBitBase); 383 } 384 switch (data) { 385 case GetCommandByte: 386 DPRINTF(I8042, "Getting command byte.\n"); 387 writeData(commandByte); 388 break; 389 case WriteCommandByte: 390 DPRINTF(I8042, "Setting command byte.\n"); 391 lastCommand = WriteCommandByte; 392 break; 393 case CheckForPassword: 394 panic("i8042 \"Check for password\" command not implemented.\n"); 395 case LoadPassword: 396 panic("i8042 \"Load password\" command not implemented.\n"); 397 case CheckPassword: 398 panic("i8042 \"Check password\" command not implemented.\n"); 399 case DisableMouse: 400 DPRINTF(I8042, "Disabling mouse at controller.\n"); 401 commandByte.disableMouse = 1; 402 break; 403 case EnableMouse: 404 DPRINTF(I8042, "Enabling mouse at controller.\n"); 405 commandByte.disableMouse = 0; 406 break; 407 case TestMouse: 408 panic("i8042 \"Test mouse\" command not implemented.\n"); 409 case SelfTest: 410 panic("i8042 \"Self test\" command not implemented.\n"); 411 case InterfaceTest: 412 panic("i8042 \"Interface test\" command not implemented.\n"); 413 case DiagnosticDump: 414 panic("i8042 \"Diagnostic dump\" command not implemented.\n"); 415 case DisableKeyboard: 416 DPRINTF(I8042, "Disabling keyboard at controller.\n"); 417 commandByte.disableKeyboard = 1; 418 break; 419 case EnableKeyboard: 420 DPRINTF(I8042, "Enabling keyboard at controller.\n"); 421 commandByte.disableKeyboard = 0; 422 break; 423 case ReadInputPort: 424 panic("i8042 \"Read input port\" command not implemented.\n"); 425 case ContinuousPollLow: 426 panic("i8042 \"Continuous poll low\" command not implemented.\n"); 427 case ContinuousPollHigh: 428 panic("i8042 \"Continuous poll high\" command not implemented.\n"); 429 case ReadOutputPort: 430 panic("i8042 \"Read output port\" command not implemented.\n"); 431 case WriteOutputPort: 432 warn("i8042 \"Write output port\" command not implemented.\n"); 433 lastCommand = WriteOutputPort; 434 case WriteKeyboardOutputBuff: 435 warn("i8042 \"Write keyboard output buffer\" " 436 "command not implemented.\n"); 437 lastCommand = WriteKeyboardOutputBuff; 438 case WriteMouseOutputBuff: 439 DPRINTF(I8042, "Got command to write to mouse output buffer.\n"); 440 lastCommand = WriteMouseOutputBuff; 441 break; 442 case WriteToMouse: 443 DPRINTF(I8042, "Expecting mouse command.\n"); 444 lastCommand = WriteToMouse; 445 break; 446 case DisableA20: 447 panic("i8042 \"Disable A20\" command not implemented.\n"); 448 case EnableA20: 449 panic("i8042 \"Enable A20\" command not implemented.\n"); 450 case ReadTestInputs: 451 panic("i8042 \"Read test inputs\" command not implemented.\n"); 452 case SystemReset: 453 panic("i8042 \"System reset\" command not implemented.\n"); 454 default: 455 warn("Write to unknown i8042 " 456 "(keyboard controller) command port.\n"); 457 } 458 } else { 459 panic("Write to unrecognized port %#x.\n", addr); 460 } 461 pkt->makeAtomicResponse(); 462 return latency; 463} 464 465void 466X86ISA::I8042::serialize(std::ostream &os) 467{ 468 uint8_t statusRegData = statusReg.__data; 469 uint8_t commandByteData = commandByte.__data; 470 471 SERIALIZE_SCALAR(dataPort); 472 SERIALIZE_SCALAR(commandPort); 473 SERIALIZE_SCALAR(statusRegData); 474 SERIALIZE_SCALAR(commandByteData); 475 SERIALIZE_SCALAR(dataReg); 476 SERIALIZE_SCALAR(lastCommand); 477 mouse.serialize("mouse", os); 478 keyboard.serialize("keyboard", os); 479} 480 481void 482X86ISA::I8042::unserialize(Checkpoint *cp, const std::string §ion) 483{ 484 uint8_t statusRegData; 485 uint8_t commandByteData; 486 487 UNSERIALIZE_SCALAR(dataPort); 488 UNSERIALIZE_SCALAR(commandPort); 489 UNSERIALIZE_SCALAR(statusRegData); 490 UNSERIALIZE_SCALAR(commandByteData); 491 UNSERIALIZE_SCALAR(dataReg); 492 UNSERIALIZE_SCALAR(lastCommand); 493 mouse.unserialize("mouse", cp, section); 494 keyboard.unserialize("keyboard", cp, section); 495 496 statusReg.__data = statusRegData; 497 commandByte.__data = commandByteData; 498} 499 500void 501X86ISA::PS2Keyboard::serialize(const std::string &base, std::ostream &os) 502{ 503 paramOut(os, base + ".lastCommand", lastCommand); 504 int bufferSize = outBuffer.size(); 505 paramOut(os, base + ".outBuffer.size", bufferSize); 506 uint8_t *buffer = new uint8_t[bufferSize]; 507 for (int i = 0; i < bufferSize; ++i) { 508 buffer[i] = outBuffer.front(); 509 outBuffer.pop(); 510 } 511 arrayParamOut(os, base + ".outBuffer.elts", buffer, 512 bufferSize*sizeof(uint8_t)); 513 delete[] buffer; 514} 515 516void 517X86ISA::PS2Keyboard::unserialize(const std::string &base, Checkpoint *cp, 518 const std::string §ion) 519{ 520 paramIn(cp, section, base + ".lastCommand", lastCommand); 521 int bufferSize; 522 paramIn(cp, section, base + ".outBuffer.size", bufferSize); 523 uint8_t *buffer = new uint8_t[bufferSize]; 524 arrayParamIn(cp, section, base + ".outBuffer.elts", buffer, 525 bufferSize*sizeof(uint8_t)); 526 for (int i = 0; i < bufferSize; ++i) { 527 outBuffer.push(buffer[i]); 528 } 529 delete[] buffer; 530} 531 532void 533X86ISA::PS2Mouse::serialize(const std::string &base, std::ostream &os) 534{ 535 uint8_t statusData = status.__data; 536 paramOut(os, base + ".lastCommand", lastCommand); 537 int bufferSize = outBuffer.size(); 538 paramOut(os, base + ".outBuffer.size", bufferSize); 539 uint8_t *buffer = new uint8_t[bufferSize]; 540 for (int i = 0; i < bufferSize; ++i) { 541 buffer[i] = outBuffer.front(); 542 outBuffer.pop(); 543 } 544 arrayParamOut(os, base + ".outBuffer.elts", buffer, 545 bufferSize*sizeof(uint8_t)); 546 delete[] buffer; 547 paramOut(os, base + ".status", statusData); 548 paramOut(os, base + ".resolution", resolution); 549 paramOut(os, base + ".sampleRate", sampleRate); 550} 551 552void 553X86ISA::PS2Mouse::unserialize(const std::string &base, Checkpoint *cp, 554 const std::string §ion) 555{ 556 uint8_t statusData; 557 paramIn(cp, section, base + ".lastCommand", lastCommand); 558 int bufferSize; 559 paramIn(cp, section, base + ".outBuffer.size", bufferSize); 560 uint8_t *buffer = new uint8_t[bufferSize]; 561 arrayParamIn(cp, section, base + ".outBuffer.elts", buffer, 562 bufferSize*sizeof(uint8_t)); 563 for (int i = 0; i < bufferSize; ++i) { 564 outBuffer.push(buffer[i]); 565 } 566 delete[] buffer; 567 paramIn(cp, section, base + ".status", statusData); 568 paramIn(cp, section, base + ".resolution", resolution); 569 paramIn(cp, section, base + ".sampleRate", sampleRate); 570 571 status.__data = statusData; 572} 573 574X86ISA::I8042 * 575I8042Params::create() 576{ 577 return new X86ISA::I8042(this); 578} 579