i8042.cc revision 12653:4f6b6c1a8e2f
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      mouseIntPin(p->mouse_int_pin), keyboardIntPin(p->keyboard_int_pin),
54      mouse(p->mouse), keyboard(p->keyboard)
55{
56    fatal_if(!mouse, "The i8042 model requires a mouse instance");
57    fatal_if(!keyboard, "The i8042 model requires a keyboard instance");
58
59    statusReg.passedSelfTest = 1;
60    statusReg.commandLast = 1;
61    statusReg.keyboardUnlocked = 1;
62
63    commandByte.convertScanCodes = 1;
64    commandByte.passedSelfTest = 1;
65    commandByte.keyboardFullInt = 1;
66}
67
68
69AddrRangeList
70X86ISA::I8042::getAddrRanges() const
71{
72    AddrRangeList ranges;
73    // TODO: Are these really supposed to be a single byte and not 4?
74    ranges.push_back(RangeSize(dataPort, 1));
75    ranges.push_back(RangeSize(commandPort, 1));
76    return ranges;
77}
78
79void
80X86ISA::I8042::writeData(uint8_t newData, bool mouse)
81{
82    DPRINTF(I8042, "Set data %#02x.\n", newData);
83    dataReg = newData;
84    statusReg.outputFull = 1;
85    statusReg.mouseOutputFull = (mouse ? 1 : 0);
86    if (!mouse && commandByte.keyboardFullInt) {
87        DPRINTF(I8042, "Sending keyboard interrupt.\n");
88        keyboardIntPin->raise();
89        //This is a hack
90        keyboardIntPin->lower();
91    } else if (mouse && commandByte.mouseFullInt) {
92        DPRINTF(I8042, "Sending mouse interrupt.\n");
93        mouseIntPin->raise();
94        //This is a hack
95        mouseIntPin->lower();
96    }
97}
98
99uint8_t
100X86ISA::I8042::readDataOut()
101{
102    uint8_t data = dataReg;
103    statusReg.outputFull = 0;
104    statusReg.mouseOutputFull = 0;
105    if (keyboard->hostDataAvailable()) {
106        writeData(keyboard->hostRead(), false);
107    } else if (mouse->hostDataAvailable()) {
108        writeData(mouse->hostRead(), true);
109    }
110    return data;
111}
112
113Tick
114X86ISA::I8042::read(PacketPtr pkt)
115{
116    assert(pkt->getSize() == 1);
117    Addr addr = pkt->getAddr();
118    if (addr == dataPort) {
119        uint8_t data = readDataOut();
120        //DPRINTF(I8042, "Read from data port got %#02x.\n", data);
121        pkt->set<uint8_t>(data);
122    } else if (addr == commandPort) {
123        //DPRINTF(I8042, "Read status as %#02x.\n", (uint8_t)statusReg);
124        pkt->set<uint8_t>((uint8_t)statusReg);
125    } else {
126        panic("Read from unrecognized port %#x.\n", addr);
127    }
128    pkt->makeAtomicResponse();
129    return latency;
130}
131
132Tick
133X86ISA::I8042::write(PacketPtr pkt)
134{
135    assert(pkt->getSize() == 1);
136    Addr addr = pkt->getAddr();
137    uint8_t data = pkt->get<uint8_t>();
138    if (addr == dataPort) {
139        statusReg.commandLast = 0;
140        switch (lastCommand) {
141          case NoCommand:
142            keyboard->hostWrite(data);
143            if (keyboard->hostDataAvailable())
144                writeData(keyboard->hostRead(), false);
145            break;
146          case WriteToMouse:
147            mouse->hostWrite(data);
148            if (mouse->hostDataAvailable())
149                writeData(mouse->hostRead(), true);
150            break;
151          case WriteCommandByte:
152            commandByte = data;
153            DPRINTF(I8042, "Got data %#02x for \"Write "
154                    "command byte\" command.\n", data);
155            statusReg.passedSelfTest = (uint8_t)commandByte.passedSelfTest;
156            break;
157          case WriteMouseOutputBuff:
158            DPRINTF(I8042, "Got data %#02x for \"Write "
159                    "mouse output buffer\" command.\n", data);
160            writeData(data, true);
161            break;
162          case WriteKeyboardOutputBuff:
163            DPRINTF(I8042, "Got data %#02x for \"Write "
164                    "keyboad output buffer\" command.\n", data);
165            writeData(data, false);
166            break;
167          case WriteOutputPort:
168            DPRINTF(I8042, "Got data %#02x for \"Write "
169                    "output port\" command.\n", data);
170            panic_if(bits(data, 0) != 1, "Reset bit should be 1");
171            // Safe to ignore otherwise
172            break;
173          default:
174            panic("Data written for unrecognized "
175                    "command %#02x\n", lastCommand);
176        }
177        lastCommand = NoCommand;
178    } else if (addr == commandPort) {
179        DPRINTF(I8042, "Got command %#02x.\n", data);
180        statusReg.commandLast = 1;
181        // These purposefully leave off the first byte of the controller RAM
182        // so it can be handled specially.
183        if (data > ReadControllerRamBase &&
184                data < ReadControllerRamBase + RamSize) {
185            panic("Attempted to use i8042 read controller RAM command to "
186                    "get byte %d.\n", data - ReadControllerRamBase);
187        } else if (data > WriteControllerRamBase &&
188                data < WriteControllerRamBase + RamSize) {
189            panic("Attempted to use i8042 read controller RAM command to "
190                    "get byte %d.\n", data - ReadControllerRamBase);
191        } else if (data >= PulseOutputBitBase &&
192                data < PulseOutputBitBase + NumOutputBits) {
193            panic("Attempted to use i8042 pulse output bit command to "
194                    "to pulse bit %d.\n", data - PulseOutputBitBase);
195        }
196        switch (data) {
197          case GetCommandByte:
198            DPRINTF(I8042, "Getting command byte.\n");
199            writeData(commandByte);
200            break;
201          case WriteCommandByte:
202            DPRINTF(I8042, "Setting command byte.\n");
203            lastCommand = WriteCommandByte;
204            break;
205          case CheckForPassword:
206            panic("i8042 \"Check for password\" command not implemented.\n");
207          case LoadPassword:
208            panic("i8042 \"Load password\" command not implemented.\n");
209          case CheckPassword:
210            panic("i8042 \"Check password\" command not implemented.\n");
211          case DisableMouse:
212            DPRINTF(I8042, "Disabling mouse at controller.\n");
213            commandByte.disableMouse = 1;
214            break;
215          case EnableMouse:
216            DPRINTF(I8042, "Enabling mouse at controller.\n");
217            commandByte.disableMouse = 0;
218            break;
219          case TestMouse:
220            panic("i8042 \"Test mouse\" command not implemented.\n");
221          case SelfTest:
222            panic("i8042 \"Self test\" command not implemented.\n");
223          case InterfaceTest:
224            panic("i8042 \"Interface test\" command not implemented.\n");
225          case DiagnosticDump:
226            panic("i8042 \"Diagnostic dump\" command not implemented.\n");
227          case DisableKeyboard:
228            DPRINTF(I8042, "Disabling keyboard at controller.\n");
229            commandByte.disableKeyboard = 1;
230            break;
231          case EnableKeyboard:
232            DPRINTF(I8042, "Enabling keyboard at controller.\n");
233            commandByte.disableKeyboard = 0;
234            break;
235          case ReadInputPort:
236            panic("i8042 \"Read input port\" command not implemented.\n");
237          case ContinuousPollLow:
238            panic("i8042 \"Continuous poll low\" command not implemented.\n");
239          case ContinuousPollHigh:
240            panic("i8042 \"Continuous poll high\" command not implemented.\n");
241          case ReadOutputPort:
242            panic("i8042 \"Read output port\" command not implemented.\n");
243          case WriteOutputPort:
244            lastCommand = WriteOutputPort;
245            break;
246          case WriteKeyboardOutputBuff:
247            lastCommand = WriteKeyboardOutputBuff;
248            break;
249          case WriteMouseOutputBuff:
250            DPRINTF(I8042, "Got command to write to mouse output buffer.\n");
251            lastCommand = WriteMouseOutputBuff;
252            break;
253          case WriteToMouse:
254            DPRINTF(I8042, "Expecting mouse command.\n");
255            lastCommand = WriteToMouse;
256            break;
257          case DisableA20:
258            panic("i8042 \"Disable A20\" command not implemented.\n");
259          case EnableA20:
260            panic("i8042 \"Enable A20\" command not implemented.\n");
261          case ReadTestInputs:
262            panic("i8042 \"Read test inputs\" command not implemented.\n");
263          case SystemReset:
264            panic("i8042 \"System reset\" command not implemented.\n");
265          default:
266            warn("Write to unknown i8042 "
267                    "(keyboard controller) command port.\n");
268        }
269    } else {
270        panic("Write to unrecognized port %#x.\n", addr);
271    }
272    pkt->makeAtomicResponse();
273    return latency;
274}
275
276void
277X86ISA::I8042::serialize(CheckpointOut &cp) const
278{
279    SERIALIZE_SCALAR(dataPort);
280    SERIALIZE_SCALAR(commandPort);
281    SERIALIZE_SCALAR(statusReg);
282    SERIALIZE_SCALAR(commandByte);
283    SERIALIZE_SCALAR(dataReg);
284    SERIALIZE_SCALAR(lastCommand);
285}
286
287void
288X86ISA::I8042::unserialize(CheckpointIn &cp)
289{
290    UNSERIALIZE_SCALAR(dataPort);
291    UNSERIALIZE_SCALAR(commandPort);
292    UNSERIALIZE_SCALAR(statusReg);
293    UNSERIALIZE_SCALAR(commandByte);
294    UNSERIALIZE_SCALAR(dataReg);
295    UNSERIALIZE_SCALAR(lastCommand);
296}
297
298X86ISA::I8042 *
299I8042Params::create()
300{
301    return new X86ISA::I8042(this);
302}
303