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