The Pedigree Project 0.1
Ps2Controller.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "Ps2Controller.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/machine/Controller.h"
24#include "pedigree/kernel/machine/Device.h"
25#include "pedigree/kernel/machine/IrqManager.h"
26#include "pedigree/kernel/machine/Machine.h"
27#include "pedigree/kernel/machine/Trace.h"
28#include "pedigree/kernel/process/Scheduler.h"
29#include "pedigree/kernel/process/Thread.h"
30#include "pedigree/kernel/processor/IoBase.h"
31#include "pedigree/kernel/processor/Processor.h"
32#include "pedigree/kernel/processor/ProcessorInformation.h"
33#include "pedigree/kernel/utilities/Vector.h"
34#include "pedigree/kernel/utilities/assert.h"
35
36static constexpr size_t Ps2IoPollLimit = 1000000;
37
38Ps2Controller::Ps2Controller(Controller* pDev)
39 : Controller(pDev),
40 IrqHandler(),
41 m_pBase(nullptr),
42 m_bHasSecondPort(false),
43 m_FirstPortBuffer(16384),
44 m_SecondPortBuffer(16384),
45 m_FirstIrqId(0),
46 m_SecondIrqId(0),
47 m_DebugState(),
48 m_ConfigByte(0),
49 m_IoGate(),
50 m_DrainLock(),
51 m_ReadMode(PollingReadMode),
52 m_RejectedThreadIo(0),
53 m_FirstPortDrops(0),
54 m_SecondPortDrops(0),
55 m_EmptyIrqs(0) {}
56
57Ps2Controller::Ps2Controller()
58 : Controller(),
59 IrqHandler(),
60 m_pBase(nullptr),
61 m_bHasSecondPort(false),
62 m_FirstPortBuffer(16384),
63 m_SecondPortBuffer(16384),
64 m_FirstIrqId(0),
65 m_SecondIrqId(0),
66 m_DebugState(),
67 m_ConfigByte(0),
68 m_IoGate(),
69 m_DrainLock(),
70 m_ReadMode(PollingReadMode),
71 m_RejectedThreadIo(0),
72 m_FirstPortDrops(0),
73 m_SecondPortDrops(0),
74 m_EmptyIrqs(0) {}
75
76Ps2Controller::~Ps2Controller() {
77 if (m_FirstIrqId || m_SecondIrqId) {
79 }
80}
81
82void Ps2Controller::initialise() {
83 TRACE("PS2 Controller startup");
84 m_pBase = addresses()[0]->m_Io;
85 assert(m_pBase);
86
87 TRACE("PS2: disabling devices");
88 sendCommand(0xAD); // disable all devices
89 sendCommand(0xA7);
90 readByteNonBlock(); // clear output buffer
91
92 TRACE("PS2: disabling IRQs");
93 m_ConfigByte = sendCommandWithResponse(0x20);
94 m_ConfigByte = (m_ConfigByte & ~0x3) | 0x40; // disable IRQs, leave translation enabled
95 sendCommand(0x60, m_ConfigByte);
96
97 m_bHasSecondPort = (m_ConfigByte & (1 << 5)) != 0;
98
99 TRACE("PS2: performing self-test");
100 uint8_t selfTestResponse = sendCommandWithResponse(0xAA);
101 NOTICE("PS/2: self-test response: " << Hex << selfTestResponse);
102
103 // Some 8042 implementations reset their config during self-test. Restore
104 // our cached IRQ-disabled/translation state before either port is enabled.
105 sendCommand(0x60, m_ConfigByte);
106
107 // Enable both ports.
108 TRACE("PS2: enabling ports");
109 sendCommand(0xAE);
110 sendCommand(0xA8);
111 // Keep the cached command byte in sync with the port-enable commands so a
112 // later IRQ configuration write does not disable the ports again.
113 m_ConfigByte &= static_cast<uint8_t>(~((1 << 4) | (1 << 5)));
114
115 // Reset all devices.
116 TRACE("PS2: resetting first device");
117 writeFirstPort(0xFF);
118 uint8_t firstAck = 0;
119 uint8_t firstStatus = 0;
120 readFirstPort(firstAck);
121 readFirstPort(firstStatus);
122 NOTICE("PS/2: first port reset result: " << Hex << firstAck << ", " << firstStatus);
123
124 TRACE("PS2: resetting second device");
125 writeSecondPort(0xFF);
126 uint8_t secondAck = 0;
127 uint8_t secondStatus = 0;
128 uint8_t secondExtra = 0;
129 readSecondPort(secondAck);
130 readSecondPort(secondStatus);
131 readSecondPort(secondExtra);
132 NOTICE("PS/2: second port reset result: " << Hex << secondAck << ", " << secondStatus << ", "
133 << secondExtra);
134
135 TRACE("PS2: startup complete");
136}
137
139 if (m_FirstIrqId || m_SecondIrqId || !m_pBase) {
140 return false;
141 }
142
143 m_FirstPortBuffer.wipe();
144 m_SecondPortBuffer.wipe();
145 m_FirstPortBuffer.enableWrites();
146 m_SecondPortBuffer.enableWrites();
147 m_RejectedThreadIo = 0;
148 m_FirstPortDrops = 0;
149 m_SecondPortDrops = 0;
150 m_EmptyIrqs = 0;
151 m_ReadMode = BufferedReadMode;
152
153 IrqManager& irqManager = *Machine::instance().getIrqManager();
154 m_FirstIrqId = irqManager.registerIsaIrqHandler(1, this, IrqPolicy::edgeThreaded());
155 if (m_FirstIrqId && m_bHasSecondPort) {
156 m_SecondIrqId = irqManager.registerIsaIrqHandler(12, this, IrqPolicy::edgeThreaded());
157 }
158 if (!m_FirstIrqId || (m_bHasSecondPort && !m_SecondIrqId)) {
159 uninitialise();
160 return false;
161 }
162
163 return true;
164}
165
167 if (m_FirstIrqId || m_SecondIrqId) {
168 if (!configureIrqEnable(false, false)) {
169 FATAL("PS/2 teardown could not disable its interrupt sources");
170 }
171
172 // Device streaming can continue after its IRQ bits are cleared. Stop the
173 // drain loop before waiting for callbacks, even under continuous traffic.
174 m_ReadMode = StoppingReadMode;
175 IrqManager& irqManager = *Machine::instance().getIrqManager();
176 if (m_FirstIrqId) {
177 if (!irqManager.unregisterHandler(m_FirstIrqId, this)) {
178 FATAL("PS/2 teardown could not drain its first port callback");
179 }
180 m_FirstIrqId = 0;
181 }
182 if (m_SecondIrqId) {
183 if (!irqManager.unregisterHandler(m_SecondIrqId, this)) {
184 FATAL("PS/2 teardown could not drain its second port callback");
185 }
186 m_SecondIrqId = 0;
187 }
188 }
189
190 // No producer remains. Publish the terminal read mode before waking blocked
191 // readers so none can fall back to polling during teardown.
192 m_ReadMode = StoppingReadMode;
193 m_FirstPortBuffer.disableWrites();
194 m_SecondPortBuffer.disableWrites();
195}
196
197bool Ps2Controller::acquireIoForThread() {
199 m_RejectedThreadIo += 1;
200 return false;
201 }
202
203 while (!m_IoGate.tryAcquire()) {
204#if THREADS
205 Thread* current = Processor::information().getCurrentThread();
206 if (!current || !Processor::getInterrupts()) {
207 m_RejectedThreadIo += 1;
208 return false;
209 }
210#if HOSTED
211 if (current->getHostedSignalDepth()) {
212 m_RejectedThreadIo += 1;
213 return false;
214 }
215#endif
216 // The current owner may be a preempted thread. Yielding lets it finish
217 // without turning controller configuration into a same-core spin.
219#else
220 m_RejectedThreadIo += 1;
221 return false;
222#endif
223 }
224 return true;
225}
226
227void Ps2Controller::releaseIo() {
228 m_IoGate.release();
229}
230
231bool Ps2Controller::sendCommandLocked(uint8_t command) {
232 if (!waitForWritingLocked())
233 return false;
234 m_pBase->write8(command, 4);
235 return true;
236}
237
238bool Ps2Controller::sendCommandLocked(uint8_t command, uint8_t data) {
239 if (!sendCommandLocked(command) || !waitForWritingLocked())
240 return false;
241 m_pBase->write8(data, 0);
242 return true;
243}
244
245bool Ps2Controller::sendCommandWithResponseLocked(uint8_t command, uint8_t& response) {
246 if (!sendCommandLocked(command) || !waitForReadingLocked())
247 return false;
248 response = m_pBase->read8(0);
249 return true;
250}
251
252bool Ps2Controller::sendCommandWithResponseLocked(uint8_t command, uint8_t data,
253 uint8_t& response) {
254 if (!sendCommandLocked(command, data) || !waitForReadingLocked())
255 return false;
256 response = m_pBase->read8(0);
257 return true;
258}
259
260bool Ps2Controller::writeFirstPortLocked(uint8_t byte) {
261 if (!waitForWritingLocked())
262 return false;
263 m_pBase->write8(byte, 0);
264 return true;
265}
266
267void Ps2Controller::sendCommand(uint8_t command) {
268 if (!acquireIoForThread()) {
269 return;
270 }
271 (void)sendCommandLocked(command);
272 releaseIo();
273}
274
275void Ps2Controller::sendCommand(uint8_t command, uint8_t data) {
276 if (!acquireIoForThread()) {
277 return;
278 }
279 (void)sendCommandLocked(command, data);
280 releaseIo();
281}
282
284 if (!acquireIoForThread()) {
285 return 0;
286 }
287 uint8_t response = 0;
288 (void)sendCommandWithResponseLocked(command, response);
289 releaseIo();
290 return response;
291}
292
293uint8_t Ps2Controller::sendCommandWithResponse(uint8_t command, uint8_t data) {
294 if (!acquireIoForThread()) {
295 return 0;
296 }
297 uint8_t response = 0;
298 (void)sendCommandWithResponseLocked(command, data, response);
299 releaseIo();
300 return response;
301}
302
304 if (!acquireIoForThread()) {
305 return;
306 }
307 (void)writeFirstPortLocked(byte);
308 releaseIo();
309}
310
312 if (!acquireIoForThread()) {
313 return;
314 }
315 (void)sendCommandLocked(0xD4, byte);
316 releaseIo();
317}
318
320 return m_bHasSecondPort;
321}
322
323void Ps2Controller::setIrqEnable(bool firstEnabled, bool secondEnabled) {
324 if (m_DebugState.active()) {
325 return;
326 }
327
328 configureIrqEnable(firstEnabled, secondEnabled);
329}
330
331bool Ps2Controller::configureIrqEnable(bool firstEnabled, bool secondEnabled) {
332 if (!acquireIoForThread()) {
333 return false;
334 }
335
336 IrqManager& irqManager = *Machine::instance().getIrqManager();
337
338 // Keep controller configuration atomic with respect to both port workers.
339 secondEnabled = secondEnabled && m_bHasSecondPort;
340 irqManager.enable(1, false);
341 irqManager.enable(12, false);
342
343 // Never accidentally remove translation
344 uint8_t flagAdd = 0x40, flagRemove = ~0;
345 if (firstEnabled) {
346 flagAdd |= 1;
347 } else {
348 flagRemove &= ~1;
349 }
350 if (secondEnabled) {
351 flagAdd |= 2;
352 } else {
353 flagRemove &= ~2;
354 }
355
356 NOTICE("Old config byte: " << Hex << m_ConfigByte);
357 m_ConfigByte |= flagAdd;
358 m_ConfigByte &= flagRemove;
359 NOTICE("New config byte: " << Hex << m_ConfigByte);
360 if (!sendCommandLocked(0x60, m_ConfigByte)) {
361 releaseIo();
362 return false;
363 }
364 NOTICE("completed!");
365
366 // re-enable now that we're done here
367 irqManager.enable(1, firstEnabled);
368 irqManager.enable(12, secondEnabled);
369 releaseIo();
370 return true;
371}
372
374 if (m_DebugState.active()) {
375 // KDB may have interrupted the current gate owner and may still carry
376 // the hard-IRQ marker. A single probe is safe; waiting is not.
377 return readByteNonBlock();
378 }
379
380 if (!acquireIoForThread()) {
381 return 0;
382 }
383 if (!waitForReadingLocked()) {
384 releaseIo();
385 return 0;
386 }
387 const uint8_t result = m_pBase->read8();
388 releaseIo();
389 return result;
390}
391
392uint8_t Ps2Controller::readByteNonBlock() {
393 if (!m_IoGate.tryAcquire()) {
394 return 0;
395 }
396 if ((m_pBase->read8(4) & 1) == 0) {
397 releaseIo();
398 return 0;
399 }
400 const uint8_t result = m_pBase->read8();
401 releaseIo();
402 return result;
403}
404
405bool Ps2Controller::readFirstPort(uint8_t& byte, bool block) {
406 if (m_DebugState.active()) {
407 byte = readByteNonBlock();
408 return byte != 0;
409 }
410
411 const size_t readMode = m_ReadMode.value();
412 if (readMode == StoppingReadMode) {
413 return false;
414 }
415 if (readMode == PollingReadMode) {
416 byte = readByte();
417 return true;
418 }
419
420 size_t numRead = m_FirstPortBuffer.read(&byte, 1, block);
421 return numRead > 0;
422}
423
424bool Ps2Controller::readSecondPort(uint8_t& byte, bool block) {
425 if (m_DebugState.active()) {
426 byte = readByteNonBlock();
427 return byte != 0;
428 }
429
430 const size_t readMode = m_ReadMode.value();
431 if (readMode == StoppingReadMode) {
432 return false;
433 }
434 if (readMode == PollingReadMode) {
435 byte = readByte();
436 return true;
437 }
438
439 size_t numRead = m_SecondPortBuffer.read(&byte, 1, block);
440 return numRead > 0;
441}
442
443void Ps2Controller::setDebugState(bool debugState) {
444 m_DebugState.set(debugState);
445}
446
448 (void)number;
449 // Both IRQ workers share port 0x60. Keep capture and delivery ordered even
450 // when a worker for the other port has already drained this occurrence.
451 LockGuard<Mutex> drain(m_DrainLock);
452 size_t batch = 0;
453 bool received = false;
454 while (m_ReadMode.value() != StoppingReadMode && !m_DebugState.active()) {
455 if (!acquireIoForThread()) {
456 return IrqDisposition::NotHandled;
457 }
458 const uint8_t status = m_pBase->read8(4);
459 if (!(status & OutputBufferFull)) {
460 releaseIo();
461 break;
462 }
463 const uint8_t value = m_pBase->read8(0);
464 const bool secondPort = (status & SecondPortData) != 0;
465 releaseIo();
466 received = true;
467
468 Buffer<uint8_t>& destination = secondPort ? m_SecondPortBuffer : m_FirstPortBuffer;
469 // Reader lock contention must not discard a release, but an unconsumed
470 // auxiliary port must not stop this shared worker by filling its buffer.
471 if (destination.writeAvailable(&value, 1) != 1) {
472 if (secondPort) {
473 m_SecondPortDrops += 1;
474 } else {
475 m_FirstPortDrops += 1;
476 }
477 }
478
479 if (++batch == 256) {
480 // Retain the drain obligation across a scheduling turn: returning with
481 // OBF set could strand data if its edge was already coalesced.
483 batch = 0;
484 }
485 }
486 if (!received) {
487 m_EmptyIrqs += 1;
488 }
489 return m_ReadMode.value() == StoppingReadMode ? IrqDisposition::Quiesced
490 : IrqDisposition::Handled;
491}
492
493bool Ps2Controller::waitForReadingLocked() {
494 // Wait for the controller's output buffer to fill.
495 for (size_t poll = 0; poll < Ps2IoPollLimit; ++poll) {
496 if (m_pBase->read8(4) & OutputBufferFull)
497 return true;
499 }
500 if (m_pBase->read8(4) & OutputBufferFull)
501 return true;
502 ERROR("PS/2 controller output buffer did not fill before timeout");
503 return false;
504}
505
506bool Ps2Controller::waitForWritingLocked() {
507 // Wait for the controller's input buffer to empty.
508 for (size_t poll = 0; poll < Ps2IoPollLimit; ++poll) {
509 if (!(m_pBase->read8(4) & InputBufferFull))
510 return true;
512 }
513 if (!(m_pBase->read8(4) & InputBufferFull))
514 return true;
515 ERROR("PS/2 controller input buffer did not empty before timeout");
516 return false;
517}
void wipe()
Definition Buffer.cc:569
void disableWrites()
Definition Buffer.cc:406
size_t writeAvailable(const T *buffer, size_t count, bool atomic=false)
Definition Buffer.cc:201
bool enableWrites()
Definition Buffer.cc:438
size_t read(T *buffer, size_t count, bool block=true)
Definition Buffer.cc:296
virtual Vector< Address * > & addresses()
Definition Device.h:266
virtual uint8_t read8(size_t offset=0)=0
virtual void write8(uint8_t value, size_t offset=0)=0
virtual irq_id_t registerIsaIrqHandler(uint8_t irq, IrqHandler *handler, const IrqPolicy &policy)=0
virtual bool unregisterHandler(irq_id_t Id, IrqHandlerBase *handler)=0
static bool getInterrupts()
static ProcessorInformation & information()
static void pause()
static bool inDeviceHardIrq()
Definition Processor.h:581
void sendCommand(uint8_t command)
Send a command with no response or data. A timed-out command is dropped.
uint8_t readByte()
Reads a single byte from the PS/2 controller by polling.
void writeFirstPort(uint8_t byte)
Send a byte to the first port, dropping it if the controller times out.
EXPORTED_PUBLIC void writeSecondPort(uint8_t byte)
Send a byte to the second port, dropping it on timeout.
IrqDisposition irq(irq_id_t number) override
bool readFirstPort(uint8_t &byte, bool block=true)
Reads a single byte from the first port.
EXPORTED_PUBLIC bool hasSecondPort() const
Reports whether this PS/2 controller has two ports.
EXPORTED_PUBLIC void setIrqEnable(bool firstEnabled, bool secondEnabled)
Enables/disables IRQs for the first or second ports.
uint8_t sendCommandWithResponse(uint8_t command)
Send a command and report its response, or zero if the operation fails.
EXPORTED_PUBLIC bool readSecondPort(uint8_t &byte, bool block=true)
Reads a single byte from the second port.
void setDebugState(bool debugState)
Selects trap-safe polling without changing controller IRQ state.
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:236
#define assert(x)
Definition assert.h:39
@ Hex
Definition Log.h:124
IrqDisposition
Definition IrqHandler.h:31