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