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