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