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