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