i8042.cc revision 12392:e0dbdf30a2a5
112855Sgabeblack@google.com/*
212855Sgabeblack@google.com * Copyright (c) 2008 The Regents of The University of Michigan
312855Sgabeblack@google.com * All rights reserved.
412855Sgabeblack@google.com *
512855Sgabeblack@google.com * Redistribution and use in source and binary forms, with or without
612855Sgabeblack@google.com * modification, are permitted provided that the following conditions are
712855Sgabeblack@google.com * met: redistributions of source code must retain the above copyright
812855Sgabeblack@google.com * notice, this list of conditions and the following disclaimer;
912855Sgabeblack@google.com * redistributions in binary form must reproduce the above copyright
1012855Sgabeblack@google.com * notice, this list of conditions and the following disclaimer in the
1112855Sgabeblack@google.com * documentation and/or other materials provided with the distribution;
1212855Sgabeblack@google.com * neither the name of the copyright holders nor the names of its
1312855Sgabeblack@google.com * contributors may be used to endorse or promote products derived from
1412855Sgabeblack@google.com * this software without specific prior written permission.
1512855Sgabeblack@google.com *
1612855Sgabeblack@google.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1712855Sgabeblack@google.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1812855Sgabeblack@google.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1912855Sgabeblack@google.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2012855Sgabeblack@google.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2112855Sgabeblack@google.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2212855Sgabeblack@google.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2312855Sgabeblack@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2412855Sgabeblack@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2512855Sgabeblack@google.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2612855Sgabeblack@google.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2712855Sgabeblack@google.com *
2812855Sgabeblack@google.com * Authors: Gabe Black
2912855Sgabeblack@google.com */
3012855Sgabeblack@google.com
3112855Sgabeblack@google.com#include "dev/x86/i8042.hh"
3212855Sgabeblack@google.com
3312855Sgabeblack@google.com#include "base/bitunion.hh"
3412855Sgabeblack@google.com#include "debug/I8042.hh"
3512855Sgabeblack@google.com#include "mem/packet.hh"
3612855Sgabeblack@google.com#include "mem/packet_access.hh"
3712855Sgabeblack@google.com
3812855Sgabeblack@google.com// The 8042 has a whopping 32 bytes of internal RAM.
3912855Sgabeblack@google.comconst uint8_t RamSize = 32;
4012855Sgabeblack@google.comconst uint8_t NumOutputBits = 14;
4112855Sgabeblack@google.comconst uint8_t X86ISA::PS2Keyboard::ID[] = {0xab, 0x83};
4212855Sgabeblack@google.comconst uint8_t X86ISA::PS2Mouse::ID[] = {0x00};
4312855Sgabeblack@google.comconst uint8_t CommandAck = 0xfa;
4412855Sgabeblack@google.comconst uint8_t CommandNack = 0xfe;
4512855Sgabeblack@google.comconst uint8_t BatSuccessful = 0xaa;
4612855Sgabeblack@google.com
4712855Sgabeblack@google.com
4812855Sgabeblack@google.comX86ISA::I8042::I8042(Params *p)
4912855Sgabeblack@google.com    : BasicPioDevice(p, 0), // pioSize arg is dummy value... not used
5012855Sgabeblack@google.com      latency(p->pio_latency),
5112855Sgabeblack@google.com      dataPort(p->data_port), commandPort(p->command_port),
5212855Sgabeblack@google.com      statusReg(0), commandByte(0), dataReg(0), lastCommand(NoCommand),
5312855Sgabeblack@google.com      mouseIntPin(p->mouse_int_pin), keyboardIntPin(p->keyboard_int_pin)
5412855Sgabeblack@google.com{
5512855Sgabeblack@google.com    statusReg.passedSelfTest = 1;
5612855Sgabeblack@google.com    statusReg.commandLast = 1;
5712855Sgabeblack@google.com    statusReg.keyboardUnlocked = 1;
5812855Sgabeblack@google.com
5912855Sgabeblack@google.com    commandByte.convertScanCodes = 1;
6012855Sgabeblack@google.com    commandByte.passedSelfTest = 1;
6112855Sgabeblack@google.com    commandByte.keyboardFullInt = 1;
6212855Sgabeblack@google.com}
6312855Sgabeblack@google.com
6412855Sgabeblack@google.com
65AddrRangeList
66X86ISA::I8042::getAddrRanges() const
67{
68    AddrRangeList ranges;
69    // TODO: Are these really supposed to be a single byte and not 4?
70    ranges.push_back(RangeSize(dataPort, 1));
71    ranges.push_back(RangeSize(commandPort, 1));
72    return ranges;
73}
74
75void
76X86ISA::I8042::writeData(uint8_t newData, bool mouse)
77{
78    DPRINTF(I8042, "Set data %#02x.\n", newData);
79    dataReg = newData;
80    statusReg.outputFull = 1;
81    statusReg.mouseOutputFull = (mouse ? 1 : 0);
82    if (!mouse && commandByte.keyboardFullInt) {
83        DPRINTF(I8042, "Sending keyboard interrupt.\n");
84        keyboardIntPin->raise();
85        //This is a hack
86        keyboardIntPin->lower();
87    } else if (mouse && commandByte.mouseFullInt) {
88        DPRINTF(I8042, "Sending mouse interrupt.\n");
89        mouseIntPin->raise();
90        //This is a hack
91        mouseIntPin->lower();
92    }
93}
94
95void
96X86ISA::PS2Device::serialize(const std::string &base, CheckpointOut &cp) const
97{
98    paramOut(cp, base + ".lastCommand", lastCommand);
99
100    std::vector<uint8_t> buffer(outBuffer.size());
101    std::copy(outBuffer.begin(), outBuffer.end(), buffer.begin());
102    arrayParamOut(cp, base + ".outBuffer.elts", buffer);
103}
104
105void
106X86ISA::PS2Device::unserialize(const std::string &base, CheckpointIn &cp)
107{
108    paramIn(cp, base + ".lastCommand", lastCommand);
109
110    std::vector<uint8_t> buffer;
111    arrayParamIn(cp, base + ".outBuffer.elts", buffer);
112    assert(outBuffer.empty());
113    for (auto c : buffer)
114        outBuffer.push_back(c);
115}
116
117
118void
119X86ISA::PS2Device::ack()
120{
121    bufferData(&CommandAck, sizeof(CommandAck));
122}
123
124void
125X86ISA::PS2Device::nack()
126{
127    bufferData(&CommandNack, sizeof(CommandNack));
128}
129
130void
131X86ISA::PS2Device::bufferData(const uint8_t *data, int size)
132{
133    assert(data || size == 0);
134    while (size) {
135        outBuffer.push_back(*(data++));
136        size--;
137    }
138}
139
140uint8_t
141X86ISA::I8042::readDataOut()
142{
143    uint8_t data = dataReg;
144    statusReg.outputFull = 0;
145    statusReg.mouseOutputFull = 0;
146    if (keyboard.hasData()) {
147        writeData(keyboard.getData(), false);
148    } else if (mouse.hasData()) {
149        writeData(mouse.getData(), true);
150    }
151    return data;
152}
153
154bool
155X86ISA::PS2Keyboard::processData(uint8_t data)
156{
157    if (lastCommand != NoCommand) {
158        switch (lastCommand) {
159          case LEDWrite:
160            DPRINTF(I8042, "Setting LEDs: "
161                    "caps lock %s, num lock %s, scroll lock %s\n",
162                    bits(data, 2) ? "on" : "off",
163                    bits(data, 1) ? "on" : "off",
164                    bits(data, 0) ? "on" : "off");
165            ack();
166            lastCommand = NoCommand;
167            break;
168          case TypematicInfo:
169            DPRINTF(I8042, "Setting typematic info to %#02x.\n", data);
170            ack();
171            lastCommand = NoCommand;
172            break;
173        }
174        return hasData();
175    }
176    switch (data) {
177      case LEDWrite:
178        DPRINTF(I8042, "Got LED write command.\n");
179        ack();
180        lastCommand = LEDWrite;
181        break;
182      case DiagnosticEcho:
183        panic("Keyboard diagnostic echo unimplemented.\n");
184      case AlternateScanCodes:
185        panic("Accessing alternate scan codes unimplemented.\n");
186      case ReadID:
187        DPRINTF(I8042, "Got keyboard read ID command.\n");
188        ack();
189        bufferData((uint8_t *)&ID, sizeof(ID));
190        break;
191      case TypematicInfo:
192        DPRINTF(I8042, "Setting typematic info.\n");
193        ack();
194        lastCommand = TypematicInfo;
195        break;
196      case Enable:
197        DPRINTF(I8042, "Enabling the keyboard.\n");
198        ack();
199        break;
200      case Disable:
201        DPRINTF(I8042, "Disabling the keyboard.\n");
202        ack();
203        break;
204      case DefaultsAndDisable:
205        DPRINTF(I8042, "Disabling and resetting the keyboard.\n");
206        ack();
207        break;
208      case AllKeysToTypematic:
209        panic("Setting all keys to typemantic unimplemented.\n");
210      case AllKeysToMakeRelease:
211        panic("Setting all keys to make/release unimplemented.\n");
212      case AllKeysToMake:
213        panic("Setting all keys to make unimplemented.\n");
214      case AllKeysToTypematicMakeRelease:
215        panic("Setting all keys to "
216                "typematic/make/release unimplemented.\n");
217      case KeyToTypematic:
218        panic("Setting a key to typematic unimplemented.\n");
219      case KeyToMakeRelease:
220        panic("Setting a key to make/release unimplemented.\n");
221      case KeyToMakeOnly:
222        panic("Setting key to make only unimplemented.\n");
223      case Resend:
224        panic("Keyboard resend unimplemented.\n");
225      case Reset:
226        panic("Keyboard reset unimplemented.\n");
227      default:
228        panic("Unknown keyboard command %#02x.\n", data);
229    }
230    return hasData();
231}
232
233bool
234X86ISA::PS2Mouse::processData(uint8_t data)
235{
236    if (lastCommand != NoCommand) {
237        switch(lastCommand) {
238          case SetResolution:
239            DPRINTF(I8042, "Mouse resolution set to %d.\n", data);
240            resolution = data;
241            ack();
242            lastCommand = NoCommand;
243            break;
244          case SampleRate:
245            DPRINTF(I8042, "Mouse sample rate %d samples "
246                    "per second.\n", data);
247            sampleRate = data;
248            ack();
249            lastCommand = NoCommand;
250            break;
251          default:
252            panic("Not expecting data for a mouse command.\n");
253        }
254        return hasData();
255    }
256    switch (data) {
257      case Scale1to1:
258        DPRINTF(I8042, "Setting mouse scale to 1:1.\n");
259        status.twoToOne = 0;
260        ack();
261        break;
262      case Scale2to1:
263        DPRINTF(I8042, "Setting mouse scale to 2:1.\n");
264        status.twoToOne = 1;
265        ack();
266        break;
267      case SetResolution:
268        DPRINTF(I8042, "Setting mouse resolution.\n");
269        lastCommand = SetResolution;
270        ack();
271        break;
272      case GetStatus:
273        DPRINTF(I8042, "Getting mouse status.\n");
274        ack();
275        bufferData((uint8_t *)&(status), 1);
276        bufferData(&resolution, sizeof(resolution));
277        bufferData(&sampleRate, sizeof(sampleRate));
278        break;
279      case ReadData:
280        panic("Reading mouse data unimplemented.\n");
281      case ResetWrapMode:
282        panic("Resetting mouse wrap mode unimplemented.\n");
283      case WrapMode:
284        panic("Setting mouse wrap mode unimplemented.\n");
285      case RemoteMode:
286        panic("Setting mouse remote mode unimplemented.\n");
287      case ReadID:
288        DPRINTF(I8042, "Mouse ID requested.\n");
289        ack();
290        bufferData(ID, sizeof(ID));
291        break;
292      case SampleRate:
293        DPRINTF(I8042, "Setting mouse sample rate.\n");
294        lastCommand = SampleRate;
295        ack();
296        break;
297      case DisableReporting:
298        DPRINTF(I8042, "Disabling data reporting.\n");
299        status.enabled = 0;
300        ack();
301        break;
302      case EnableReporting:
303        DPRINTF(I8042, "Enabling data reporting.\n");
304        status.enabled = 1;
305        ack();
306        break;
307      case DefaultsAndDisable:
308        DPRINTF(I8042, "Disabling and resetting mouse.\n");
309        sampleRate = 100;
310        resolution = 4;
311        status.twoToOne = 0;
312        status.enabled = 0;
313        ack();
314        break;
315      case Resend:
316        panic("Mouse resend unimplemented.\n");
317      case Reset:
318        DPRINTF(I8042, "Resetting the mouse.\n");
319        sampleRate = 100;
320        resolution = 4;
321        status.twoToOne = 0;
322        status.enabled = 0;
323        ack();
324        bufferData(&BatSuccessful, sizeof(BatSuccessful));
325        bufferData(ID, sizeof(ID));
326        break;
327      default:
328        warn("Unknown mouse command %#02x.\n", data);
329        nack();
330        break;
331    }
332    return hasData();
333}
334
335
336Tick
337X86ISA::I8042::read(PacketPtr pkt)
338{
339    assert(pkt->getSize() == 1);
340    Addr addr = pkt->getAddr();
341    if (addr == dataPort) {
342        uint8_t data = readDataOut();
343        //DPRINTF(I8042, "Read from data port got %#02x.\n", data);
344        pkt->set<uint8_t>(data);
345    } else if (addr == commandPort) {
346        //DPRINTF(I8042, "Read status as %#02x.\n", (uint8_t)statusReg);
347        pkt->set<uint8_t>((uint8_t)statusReg);
348    } else {
349        panic("Read from unrecognized port %#x.\n", addr);
350    }
351    pkt->makeAtomicResponse();
352    return latency;
353}
354
355Tick
356X86ISA::I8042::write(PacketPtr pkt)
357{
358    assert(pkt->getSize() == 1);
359    Addr addr = pkt->getAddr();
360    uint8_t data = pkt->get<uint8_t>();
361    if (addr == dataPort) {
362        statusReg.commandLast = 0;
363        switch (lastCommand) {
364          case NoCommand:
365            if (keyboard.processData(data)) {
366                writeData(keyboard.getData(), false);
367            }
368            break;
369          case WriteToMouse:
370            if (mouse.processData(data)) {
371                writeData(mouse.getData(), true);
372            }
373            break;
374          case WriteCommandByte:
375            commandByte = data;
376            DPRINTF(I8042, "Got data %#02x for \"Write "
377                    "command byte\" command.\n", data);
378            statusReg.passedSelfTest = (uint8_t)commandByte.passedSelfTest;
379            break;
380          case WriteMouseOutputBuff:
381            DPRINTF(I8042, "Got data %#02x for \"Write "
382                    "mouse output buffer\" command.\n", data);
383            writeData(data, true);
384            break;
385          default:
386            panic("Data written for unrecognized "
387                    "command %#02x\n", lastCommand);
388        }
389        lastCommand = NoCommand;
390    } else if (addr == commandPort) {
391        DPRINTF(I8042, "Got command %#02x.\n", data);
392        statusReg.commandLast = 1;
393        // These purposefully leave off the first byte of the controller RAM
394        // so it can be handled specially.
395        if (data > ReadControllerRamBase &&
396                data < ReadControllerRamBase + RamSize) {
397            panic("Attempted to use i8042 read controller RAM command to "
398                    "get byte %d.\n", data - ReadControllerRamBase);
399        } else if (data > WriteControllerRamBase &&
400                data < WriteControllerRamBase + RamSize) {
401            panic("Attempted to use i8042 read controller RAM command to "
402                    "get byte %d.\n", data - ReadControllerRamBase);
403        } else if (data >= PulseOutputBitBase &&
404                data < PulseOutputBitBase + NumOutputBits) {
405            panic("Attempted to use i8042 pulse output bit command to "
406                    "to pulse bit %d.\n", data - PulseOutputBitBase);
407        }
408        switch (data) {
409          case GetCommandByte:
410            DPRINTF(I8042, "Getting command byte.\n");
411            writeData(commandByte);
412            break;
413          case WriteCommandByte:
414            DPRINTF(I8042, "Setting command byte.\n");
415            lastCommand = WriteCommandByte;
416            break;
417          case CheckForPassword:
418            panic("i8042 \"Check for password\" command not implemented.\n");
419          case LoadPassword:
420            panic("i8042 \"Load password\" command not implemented.\n");
421          case CheckPassword:
422            panic("i8042 \"Check password\" command not implemented.\n");
423          case DisableMouse:
424            DPRINTF(I8042, "Disabling mouse at controller.\n");
425            commandByte.disableMouse = 1;
426            break;
427          case EnableMouse:
428            DPRINTF(I8042, "Enabling mouse at controller.\n");
429            commandByte.disableMouse = 0;
430            break;
431          case TestMouse:
432            panic("i8042 \"Test mouse\" command not implemented.\n");
433          case SelfTest:
434            panic("i8042 \"Self test\" command not implemented.\n");
435          case InterfaceTest:
436            panic("i8042 \"Interface test\" command not implemented.\n");
437          case DiagnosticDump:
438            panic("i8042 \"Diagnostic dump\" command not implemented.\n");
439          case DisableKeyboard:
440            DPRINTF(I8042, "Disabling keyboard at controller.\n");
441            commandByte.disableKeyboard = 1;
442            break;
443          case EnableKeyboard:
444            DPRINTF(I8042, "Enabling keyboard at controller.\n");
445            commandByte.disableKeyboard = 0;
446            break;
447          case ReadInputPort:
448            panic("i8042 \"Read input port\" command not implemented.\n");
449          case ContinuousPollLow:
450            panic("i8042 \"Continuous poll low\" command not implemented.\n");
451          case ContinuousPollHigh:
452            panic("i8042 \"Continuous poll high\" command not implemented.\n");
453          case ReadOutputPort:
454            panic("i8042 \"Read output port\" command not implemented.\n");
455          case WriteOutputPort:
456            warn("i8042 \"Write output port\" command not implemented.\n");
457            lastCommand = WriteOutputPort;
458            break;
459          case WriteKeyboardOutputBuff:
460            warn("i8042 \"Write keyboard output buffer\" "
461                    "command not implemented.\n");
462            lastCommand = WriteKeyboardOutputBuff;
463            break;
464          case WriteMouseOutputBuff:
465            DPRINTF(I8042, "Got command to write to mouse output buffer.\n");
466            lastCommand = WriteMouseOutputBuff;
467            break;
468          case WriteToMouse:
469            DPRINTF(I8042, "Expecting mouse command.\n");
470            lastCommand = WriteToMouse;
471            break;
472          case DisableA20:
473            panic("i8042 \"Disable A20\" command not implemented.\n");
474          case EnableA20:
475            panic("i8042 \"Enable A20\" command not implemented.\n");
476          case ReadTestInputs:
477            panic("i8042 \"Read test inputs\" command not implemented.\n");
478          case SystemReset:
479            panic("i8042 \"System reset\" command not implemented.\n");
480          default:
481            warn("Write to unknown i8042 "
482                    "(keyboard controller) command port.\n");
483        }
484    } else {
485        panic("Write to unrecognized port %#x.\n", addr);
486    }
487    pkt->makeAtomicResponse();
488    return latency;
489}
490
491void
492X86ISA::I8042::serialize(CheckpointOut &cp) const
493{
494    uint8_t statusRegData = statusReg.__data;
495    uint8_t commandByteData = commandByte.__data;
496
497    SERIALIZE_SCALAR(dataPort);
498    SERIALIZE_SCALAR(commandPort);
499    SERIALIZE_SCALAR(statusRegData);
500    SERIALIZE_SCALAR(commandByteData);
501    SERIALIZE_SCALAR(dataReg);
502    SERIALIZE_SCALAR(lastCommand);
503    mouse.serialize("mouse", cp);
504    keyboard.serialize("keyboard", cp);
505}
506
507void
508X86ISA::I8042::unserialize(CheckpointIn &cp)
509{
510    uint8_t statusRegData;
511    uint8_t commandByteData;
512
513    UNSERIALIZE_SCALAR(dataPort);
514    UNSERIALIZE_SCALAR(commandPort);
515    UNSERIALIZE_SCALAR(statusRegData);
516    UNSERIALIZE_SCALAR(commandByteData);
517    UNSERIALIZE_SCALAR(dataReg);
518    UNSERIALIZE_SCALAR(lastCommand);
519    mouse.unserialize("mouse", cp);
520    keyboard.unserialize("keyboard", cp);
521
522    statusReg.__data = statusRegData;
523    commandByte.__data = commandByteData;
524}
525
526void
527X86ISA::PS2Mouse::serialize(const std::string &base, CheckpointOut &cp) const
528{
529    PS2Device::serialize(base, cp);
530
531    paramOut(cp, base + ".status", status);
532    paramOut(cp, base + ".resolution", resolution);
533    paramOut(cp, base + ".sampleRate", sampleRate);
534}
535
536void
537X86ISA::PS2Mouse::unserialize(const std::string &base, CheckpointIn &cp)
538{
539    PS2Device::unserialize(base, cp);
540
541    paramIn(cp, base + ".status", status);
542    paramIn(cp, base + ".resolution", resolution);
543    paramIn(cp, base + ".sampleRate", sampleRate);
544}
545
546X86ISA::I8042 *
547I8042Params::create()
548{
549    return new X86ISA::I8042(this);
550}
551