i8042.cc revision 5831:ee307cca6d31
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 "dev/x86/i8042.hh"
33#include "mem/packet.hh"
34#include "mem/packet_access.hh"
35
36// The 8042 has a whopping 32 bytes of internal RAM.
37const uint8_t RamSize = 32;
38const uint8_t NumOutputBits = 14;
39const uint8_t KeyboardID[] = {0xab, 0x83};
40const uint8_t MouseID[] = {0x00};
41const uint8_t CommandAck = 0xfa;
42const uint8_t CommandNack = 0xfe;
43const uint8_t BatSuccessful = 0xaa;
44
45enum Port64Command
46{
47    GetCommandByte = 0x20,
48    ReadControllerRamBase = 0x20,
49    WriteCommandByte = 0x60,
50    WriteControllerRamBase = 0x60,
51    CheckForPassword = 0xA4,
52    LoadPassword = 0xA5,
53    CheckPassword = 0xA6,
54    DisableMouse = 0xA7,
55    EnableMouse = 0xA8,
56    TestMouse = 0xA9,
57    SelfTest = 0xAA,
58    InterfaceTest = 0xAB,
59    DiagnosticDump = 0xAC,
60    DisableKeyboard = 0xAD,
61    EnableKeyboard = 0xAE,
62    ReadInputPort = 0xC0,
63    ContinuousPollLow = 0xC1,
64    ContinuousPollHigh = 0xC2,
65    ReadOutputPort = 0xD0,
66    WriteOutputPort = 0xD1,
67    WriteKeyboardOutputBuff = 0xD2,
68    WriteMouseOutputBuff = 0xD3,
69    WriteToMouse = 0xD4,
70    DisableA20 = 0xDD,
71    EnableA20 = 0xDF,
72    ReadTestInputs = 0xE0,
73    PulseOutputBitBase = 0xF0,
74    SystemReset = 0xFE
75};
76
77enum Port60Command
78{
79    MouseScale1to1 = 0xE6,
80    MouseScale2to1 = 0xE7,
81    SetMouseResolution = 0xE8,
82    MouseGetStatus = 0xE9,
83    MouseReadData = 0xEB,
84    MouseResetWrapMode = 0xEC,
85    LEDWrite = 0xED,
86    DiagnosticEcho = 0xEE,
87    MouseWrapMode = 0xEE,
88    AlternateScanCodes = 0xF0,
89    MouseRemoteMode = 0xF0,
90    ReadKeyboardID = 0xF2,
91    ReadMouseID = 0xF2,
92    TypematicInfo = 0xF3,
93    MouseSampleRate = 0xF3,
94    KeyboardEnable = 0xF4,
95    MouseEnableReporting = 0xF4,
96    KeyboardDisable = 0xF5,
97    MouseDisableReporting = 0xF5,
98    DefaultsAndDisableKeyboard = 0xF6,
99    DefaultsAndDisableMouse = 0xF6,
100    AllKeysToTypematic = 0xF7,
101    AllKeysToMakeRelease = 0xF8,
102    AllKeysToMake = 0xF9,
103    AllKeysToTypematicMakeRelease = 0xFA,
104    KeyToTypematic = 0xFB,
105    KeyToMakeRelease = 0xFC,
106    KeyToMakeOnly = 0xFD,
107    Resend = 0xFE,
108    KeyboardReset = 0xFF,
109    MouseReset = 0xFF
110};
111
112void
113X86ISA::I8042::addressRanges(AddrRangeList &range_list)
114{
115    range_list.clear();
116    range_list.push_back(RangeSize(dataPort, 1));
117    range_list.push_back(RangeSize(commandPort, 1));
118}
119
120bool
121X86ISA::I8042::writeData(uint8_t newData, bool mouse)
122{
123    if (!statusReg.outputFull) {
124        DPRINTF(I8042, "Set data %#02x.\n", newData);
125        dataReg = newData;
126        statusReg.outputFull = 1;
127        statusReg.mouseOutputFull = (mouse ? 1 : 0);
128        return true;
129    } else {
130        return false;
131    }
132}
133
134void
135X86ISA::I8042::keyboardAck()
136{
137    while (!keyboardBuffer.empty())
138        keyboardBuffer.pop();
139    writeKeyboardData(&CommandAck, sizeof(CommandAck));
140}
141
142void
143X86ISA::I8042::writeKeyboardData(const uint8_t *data, int size)
144{
145    assert(data || size == 0);
146    while (size) {
147        keyboardBuffer.push(*(data++));
148        size--;
149    }
150    if (writeData(keyboardBuffer.front())) {
151        keyboardBuffer.pop();
152        if (commandByte.keyboardFullInt) {
153            DPRINTF(I8042, "Sending keyboard interrupt.\n");
154            keyboardIntPin->raise();
155            //XXX This is a hack.
156            keyboardIntPin->lower();
157        }
158    }
159}
160
161void
162X86ISA::I8042::mouseAck()
163{
164    while (!mouseBuffer.empty())
165        mouseBuffer.pop();
166    writeMouseData(&CommandAck, sizeof(CommandAck));
167}
168
169void
170X86ISA::I8042::mouseNack()
171{
172    while (!mouseBuffer.empty())
173        mouseBuffer.pop();
174    writeMouseData(&CommandNack, sizeof(CommandAck));
175}
176
177void
178X86ISA::I8042::writeMouseData(const uint8_t *data, int size)
179{
180    assert(data || size == 0);
181    while (size) {
182        mouseBuffer.push(*(data++));
183        size--;
184    }
185    if (writeData(mouseBuffer.front(), true)) {
186        mouseBuffer.pop();
187        if (commandByte.mouseFullInt) {
188            DPRINTF(I8042, "Sending mouse interrupt.\n");
189            mouseIntPin->raise();
190            //XXX This is a hack
191            mouseIntPin->lower();
192        }
193    }
194}
195
196uint8_t
197X86ISA::I8042::readDataOut()
198{
199    uint8_t data = dataReg;
200    statusReg.outputFull = 0;
201    statusReg.mouseOutputFull = 0;
202    if (!keyboardBuffer.empty()) {
203        writeKeyboardData(NULL, 0);
204    }
205    if (!mouseBuffer.empty()) {
206        writeMouseData(NULL, 0);
207    }
208    return data;
209}
210
211Tick
212X86ISA::I8042::read(PacketPtr pkt)
213{
214    assert(pkt->getSize() == 1);
215    Addr addr = pkt->getAddr();
216    if (addr == dataPort) {
217        uint8_t data = readDataOut();
218        //DPRINTF(I8042, "Read from data port got %#02x.\n", data);
219        pkt->set<uint8_t>(data);
220    } else if (addr == commandPort) {
221        //DPRINTF(I8042, "Read status as %#02x.\n", (uint8_t)statusReg);
222        pkt->set<uint8_t>((uint8_t)statusReg);
223    } else {
224        panic("Read from unrecognized port %#x.\n", addr);
225    }
226    return latency;
227}
228
229Tick
230X86ISA::I8042::write(PacketPtr pkt)
231{
232    assert(pkt->getSize() == 1);
233    Addr addr = pkt->getAddr();
234    uint8_t data = pkt->get<uint8_t>();
235    if (addr == dataPort) {
236        statusReg.commandLast = 0;
237        switch (lastCommand) {
238          case NoCommand:
239            if (lastKeyboardCommand != NoCommand) {
240                switch (lastKeyboardCommand) {
241                  case LEDWrite:
242                    DPRINTF(I8042, "Setting LEDs: "
243                            "caps lock %s, num lock %s, scroll lock %s\n",
244                            bits(data, 2) ? "on" : "off",
245                            bits(data, 1) ? "on" : "off",
246                            bits(data, 0) ? "on" : "off");
247                    keyboardAck();
248                    lastKeyboardCommand = NoCommand;
249                    break;
250                  case TypematicInfo:
251                    DPRINTF(I8042,
252                            "Setting typematic info to %#02x.\n", data);
253                    keyboardAck();
254                    lastKeyboardCommand = NoCommand;
255                    break;
256                }
257                break;
258            }
259            DPRINTF(I8042, "Got port 0x60 command %#02x.\n", data);
260            switch (data) {
261              case LEDWrite:
262                DPRINTF(I8042, "Got LED write command.\n");
263                keyboardAck();
264                lastKeyboardCommand = LEDWrite;
265                break;
266              case DiagnosticEcho:
267                panic("Keyboard diagnostic echo unimplemented.\n");
268              case AlternateScanCodes:
269                panic("Accessing alternate scan codes unimplemented.\n");
270              case ReadKeyboardID:
271                DPRINTF(I8042, "Got keyboard read ID command.\n");
272                keyboardAck();
273                writeKeyboardData((uint8_t *)&KeyboardID, sizeof(KeyboardID));
274                break;
275              case TypematicInfo:
276                DPRINTF(I8042, "Setting typematic info.\n");
277                keyboardAck();
278                lastKeyboardCommand = TypematicInfo;
279                break;
280              case KeyboardEnable:
281                DPRINTF(I8042, "Enabling the keyboard.\n");
282                keyboardAck();
283                break;
284              case KeyboardDisable:
285                DPRINTF(I8042, "Disabling the keyboard.\n");
286                keyboardAck();
287                break;
288              case DefaultsAndDisableKeyboard:
289                DPRINTF(I8042, "Disabling and resetting the keyboard.\n");
290                keyboardAck();
291                break;
292              case AllKeysToTypematic:
293                panic("Setting all keys to typemantic unimplemented.\n");
294              case AllKeysToMakeRelease:
295                panic("Setting all keys to make/release unimplemented.\n");
296              case AllKeysToMake:
297                panic("Setting all keys to make unimplemented.\n");
298              case AllKeysToTypematicMakeRelease:
299                panic("Setting all keys to "
300                        "typematic/make/release unimplemented.\n");
301              case KeyToTypematic:
302                panic("Setting a key to typematic unimplemented.\n");
303              case KeyToMakeRelease:
304                panic("Setting a key to make/release unimplemented.\n");
305              case KeyToMakeOnly:
306                panic("Setting key to make only unimplemented.\n");
307              case Resend:
308                panic("Keyboard resend unimplemented.\n");
309              case KeyboardReset:
310                panic("Keyboard reset unimplemented.\n");
311              default:
312                panic("Unknown keyboard command %#02x.\n", data);
313            }
314            break;
315          case WriteToMouse:
316            if (lastMouseCommand != NoCommand) {
317                switch(lastMouseCommand) {
318                  case SetMouseResolution:
319                    DPRINTF(I8042, "Mouse resolution set to %d.\n", data);
320                    mouseResolution = data;
321                    mouseAck();
322                    lastMouseCommand = NoCommand;
323                    break;
324                  case MouseSampleRate:
325                    DPRINTF(I8042, "Mouse sample rate %d samples "
326                            "per second.\n", data);
327                    mouseSampleRate = data;
328                    mouseAck();
329                    lastMouseCommand = NoCommand;
330                    break;
331                  default:
332                    panic("Not expecting data for a mouse command.\n");
333                }
334                break;
335            }
336            switch (data) {
337              case MouseScale1to1:
338                DPRINTF(I8042, "Setting mouse scale to 1:1.\n");
339                mouseStatus.twoToOne = 0;
340                mouseAck();
341                break;
342              case MouseScale2to1:
343                DPRINTF(I8042, "Setting mouse scale to 2:1.\n");
344                mouseStatus.twoToOne = 1;
345                mouseAck();
346                break;
347              case SetMouseResolution:
348                DPRINTF(I8042, "Setting mouse resolution.\n");
349                lastMouseCommand = SetMouseResolution;
350                mouseAck();
351                break;
352              case MouseGetStatus:
353                DPRINTF(I8042, "Getting mouse status.\n");
354                mouseAck();
355                writeMouseData((uint8_t *)&(mouseStatus), 1);
356                writeMouseData(&mouseResolution, sizeof(mouseResolution));
357                writeMouseData(&mouseSampleRate, sizeof(mouseSampleRate));
358                break;
359              case MouseReadData:
360                panic("Reading mouse data unimplemented.\n");
361              case MouseResetWrapMode:
362                panic("Resetting mouse wrap mode unimplemented.\n");
363              case MouseWrapMode:
364                panic("Setting mouse wrap mode unimplemented.\n");
365              case MouseRemoteMode:
366                panic("Setting mouse remote mode unimplemented.\n");
367              case ReadMouseID:
368                DPRINTF(I8042, "Mouse ID requested.\n");
369                mouseAck();
370                writeMouseData(MouseID, sizeof(MouseID));
371                break;
372              case MouseSampleRate:
373                DPRINTF(I8042, "Setting mouse sample rate.\n");
374                lastMouseCommand = MouseSampleRate;
375                mouseAck();
376                break;
377              case MouseDisableReporting:
378                DPRINTF(I8042, "Disabling data reporting.\n");
379                mouseStatus.enabled = 0;
380                mouseAck();
381                break;
382              case MouseEnableReporting:
383                DPRINTF(I8042, "Enabling data reporting.\n");
384                mouseStatus.enabled = 1;
385                mouseAck();
386                break;
387              case DefaultsAndDisableMouse:
388                DPRINTF(I8042, "Disabling and resetting mouse.\n");
389                mouseSampleRate = 100;
390                mouseResolution = 4;
391                mouseStatus.twoToOne = 0;
392                mouseStatus.enabled = 0;
393                mouseAck();
394                break;
395              case Resend:
396                panic("Keyboard resent unimplemented.\n");
397              case MouseReset:
398                DPRINTF(I8042, "Resetting the mouse.\n");
399                mouseSampleRate = 100;
400                mouseResolution = 4;
401                mouseStatus.twoToOne = 0;
402                mouseStatus.enabled = 0;
403                mouseAck();
404                writeMouseData(&BatSuccessful, sizeof(BatSuccessful));
405                writeMouseData(MouseID, sizeof(MouseID));
406                break;
407              default:
408                warn("Unknown mouse command %#02x.\n", data);
409                mouseNack();
410                break;
411            }
412            break;
413          case WriteCommandByte:
414            commandByte = data;
415            DPRINTF(I8042, "Got data %#02x for \"Write "
416                    "command byte\" command.\n", data);
417            statusReg.passedSelfTest = (uint8_t)commandByte.passedSelfTest;
418            break;
419          case WriteMouseOutputBuff:
420            DPRINTF(I8042, "Got data %#02x for \"Write "
421                    "mouse output buffer\" command.\n", data);
422            writeMouseData(&data, sizeof(data));
423            break;
424          default:
425            panic("Data written for unrecognized "
426                    "command %#02x\n", lastCommand);
427        }
428        lastCommand = NoCommand;
429    } else if (addr == commandPort) {
430        DPRINTF(I8042, "Got command %#02x.\n", data);
431        statusReg.commandLast = 1;
432        // These purposefully leave off the first byte of the controller RAM
433        // so it can be handled specially.
434        if (data > ReadControllerRamBase &&
435                data < ReadControllerRamBase + RamSize) {
436            panic("Attempted to use i8042 read controller RAM command to "
437                    "get byte %d.\n", data - ReadControllerRamBase);
438        } else if (data > WriteControllerRamBase &&
439                data < WriteControllerRamBase + RamSize) {
440            panic("Attempted to use i8042 read controller RAM command to "
441                    "get byte %d.\n", data - ReadControllerRamBase);
442        } else if (data >= PulseOutputBitBase &&
443                data < PulseOutputBitBase + NumOutputBits) {
444            panic("Attempted to use i8042 pulse output bit command to "
445                    "to pulse bit %d.\n", data - PulseOutputBitBase);
446        }
447        switch (data) {
448          case GetCommandByte:
449            DPRINTF(I8042, "Getting command byte.\n");
450            writeData(commandByte);
451            break;
452          case WriteCommandByte:
453            DPRINTF(I8042, "Setting command byte.\n");
454            lastCommand = WriteCommandByte;
455            break;
456          case CheckForPassword:
457            panic("i8042 \"Check for password\" command not implemented.\n");
458          case LoadPassword:
459            panic("i8042 \"Load password\" command not implemented.\n");
460          case CheckPassword:
461            panic("i8042 \"Check password\" command not implemented.\n");
462          case DisableMouse:
463            DPRINTF(I8042, "Disabling mouse at controller.\n");
464            commandByte.disableMouse = 1;
465            break;
466          case EnableMouse:
467            DPRINTF(I8042, "Enabling mouse at controller.\n");
468            commandByte.disableMouse = 0;
469            break;
470          case TestMouse:
471            panic("i8042 \"Test mouse\" command not implemented.\n");
472          case SelfTest:
473            panic("i8042 \"Self test\" command not implemented.\n");
474          case InterfaceTest:
475            panic("i8042 \"Interface test\" command not implemented.\n");
476          case DiagnosticDump:
477            panic("i8042 \"Diagnostic dump\" command not implemented.\n");
478          case DisableKeyboard:
479            DPRINTF(I8042, "Disabling keyboard at controller.\n");
480            commandByte.disableKeyboard = 1;
481            break;
482          case EnableKeyboard:
483            DPRINTF(I8042, "Enabling keyboard at controller.\n");
484            commandByte.disableKeyboard = 0;
485            break;
486          case ReadInputPort:
487            panic("i8042 \"Read input port\" command not implemented.\n");
488          case ContinuousPollLow:
489            panic("i8042 \"Continuous poll low\" command not implemented.\n");
490          case ContinuousPollHigh:
491            panic("i8042 \"Continuous poll high\" command not implemented.\n");
492          case ReadOutputPort:
493            panic("i8042 \"Read output port\" command not implemented.\n");
494          case WriteOutputPort:
495            panic("i8042 \"Write output port\" command not implemented.\n");
496          case WriteKeyboardOutputBuff:
497            panic("i8042 \"Write keyboard output buffer\" "
498                    "command not implemented.\n");
499          case WriteMouseOutputBuff:
500            DPRINTF(I8042, "Got command to write to mouse output buffer.\n");
501            lastCommand = WriteMouseOutputBuff;
502            break;
503          case WriteToMouse:
504            DPRINTF(I8042, "Expecting mouse command.\n");
505            lastCommand = WriteToMouse;
506            break;
507          case DisableA20:
508            panic("i8042 \"Disable A20\" command not implemented.\n");
509          case EnableA20:
510            panic("i8042 \"Enable A20\" command not implemented.\n");
511          case ReadTestInputs:
512            panic("i8042 \"Read test inputs\" command not implemented.\n");
513          case SystemReset:
514            panic("i8042 \"System reset\" command not implemented.\n");
515          default:
516            panic("Write to unknown i8042 "
517                    "(keyboard controller) command port.\n");
518        }
519    } else {
520        panic("Write to unrecognized port %#x.\n", addr);
521    }
522    return latency;
523}
524
525X86ISA::I8042 *
526I8042Params::create()
527{
528    return new X86ISA::I8042(this);
529}
530