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"
36static constexpr size_t Ps2IoPollLimit = 1000000;
42 m_bHasSecondPort(false),
43 m_FirstPortBuffer(16384),
44 m_SecondPortBuffer(16384),
51 m_ReadMode(PollingReadMode),
52 m_RejectedThreadIo(0),
57Ps2Controller::Ps2Controller()
61 m_bHasSecondPort(false),
62 m_FirstPortBuffer(16384),
63 m_SecondPortBuffer(16384),
70 m_ReadMode(PollingReadMode),
71 m_RejectedThreadIo(0),
76Ps2Controller::~Ps2Controller() {
77 if (m_FirstIrqId || m_SecondIrqId) {
82void Ps2Controller::initialise() {
83 TRACE(
"PS2 Controller startup");
87 TRACE(
"PS2: disabling devices");
92 TRACE(
"PS2: disabling IRQs");
94 m_ConfigByte = (m_ConfigByte & ~0x3) | 0x40;
97 m_bHasSecondPort = (m_ConfigByte & (1 << 5)) != 0;
99 TRACE(
"PS2: performing self-test");
101 NOTICE(
"PS/2: self-test response: " <<
Hex << selfTestResponse);
108 TRACE(
"PS2: enabling ports");
113 m_ConfigByte &=
static_cast<uint8_t
>(~((1 << 4) | (1 << 5)));
116 TRACE(
"PS2: resetting first device");
118 uint8_t firstAck = 0;
119 uint8_t firstStatus = 0;
122 NOTICE(
"PS/2: first port reset result: " <<
Hex << firstAck <<
", " << firstStatus);
124 TRACE(
"PS2: resetting second device");
126 uint8_t secondAck = 0;
127 uint8_t secondStatus = 0;
128 uint8_t secondExtra = 0;
132 NOTICE(
"PS/2: second port reset result: " <<
Hex << secondAck <<
", " << secondStatus <<
", "
135 TRACE(
"PS2: startup complete");
139 if (m_FirstIrqId || m_SecondIrqId || !m_pBase) {
143 m_FirstPortBuffer.
wipe();
144 m_SecondPortBuffer.
wipe();
147 m_RejectedThreadIo = 0;
148 m_FirstPortDrops = 0;
149 m_SecondPortDrops = 0;
151 m_ReadMode = BufferedReadMode;
153 IrqManager& irqManager = *Machine::instance().getIrqManager();
155 if (m_FirstIrqId && m_bHasSecondPort) {
158 if (!m_FirstIrqId || (m_bHasSecondPort && !m_SecondIrqId)) {
167 if (m_FirstIrqId || m_SecondIrqId) {
168 if (!configureIrqEnable(
false,
false)) {
169 FATAL(
"PS/2 teardown could not disable its interrupt sources");
174 m_ReadMode = StoppingReadMode;
175 IrqManager& irqManager = *Machine::instance().getIrqManager();
178 FATAL(
"PS/2 teardown could not drain its first port callback");
184 FATAL(
"PS/2 teardown could not drain its second port callback");
192 m_ReadMode = StoppingReadMode;
197bool Ps2Controller::acquireIoForThread() {
199 m_RejectedThreadIo += 1;
203 while (!m_IoGate.tryAcquire()) {
207 m_RejectedThreadIo += 1;
211 if (current->getHostedSignalDepth()) {
212 m_RejectedThreadIo += 1;
220 m_RejectedThreadIo += 1;
227void Ps2Controller::releaseIo() {
231bool Ps2Controller::sendCommandLocked(uint8_t command) {
232 if (!waitForWritingLocked())
234 m_pBase->
write8(command, 4);
238bool Ps2Controller::sendCommandLocked(uint8_t command, uint8_t data) {
239 if (!sendCommandLocked(command) || !waitForWritingLocked())
245bool Ps2Controller::sendCommandWithResponseLocked(uint8_t command, uint8_t& response) {
246 if (!sendCommandLocked(command) || !waitForReadingLocked())
248 response = m_pBase->
read8(0);
252bool Ps2Controller::sendCommandWithResponseLocked(uint8_t command, uint8_t data,
254 if (!sendCommandLocked(command, data) || !waitForReadingLocked())
256 response = m_pBase->
read8(0);
260bool Ps2Controller::writeFirstPortLocked(uint8_t
byte) {
261 if (!waitForWritingLocked())
268 if (!acquireIoForThread()) {
271 (void)sendCommandLocked(command);
276 if (!acquireIoForThread()) {
279 (void)sendCommandLocked(command, data);
284 if (!acquireIoForThread()) {
287 uint8_t response = 0;
288 (void)sendCommandWithResponseLocked(command, response);
294 if (!acquireIoForThread()) {
297 uint8_t response = 0;
298 (void)sendCommandWithResponseLocked(command, data, response);
304 if (!acquireIoForThread()) {
307 (void)writeFirstPortLocked(
byte);
312 if (!acquireIoForThread()) {
315 (void)sendCommandLocked(0xD4,
byte);
320 return m_bHasSecondPort;
324 if (m_DebugState.active()) {
328 configureIrqEnable(firstEnabled, secondEnabled);
331bool Ps2Controller::configureIrqEnable(
bool firstEnabled,
bool secondEnabled) {
332 if (!acquireIoForThread()) {
336 IrqManager& irqManager = *Machine::instance().getIrqManager();
339 secondEnabled = secondEnabled && m_bHasSecondPort;
340 irqManager.enable(1,
false);
341 irqManager.enable(12,
false);
344 uint8_t flagAdd = 0x40, flagRemove = ~0;
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)) {
364 NOTICE(
"completed!");
367 irqManager.enable(1, firstEnabled);
368 irqManager.enable(12, secondEnabled);
374 if (m_DebugState.active()) {
377 return readByteNonBlock();
380 if (!acquireIoForThread()) {
383 if (!waitForReadingLocked()) {
387 const uint8_t result = m_pBase->
read8();
392uint8_t Ps2Controller::readByteNonBlock() {
393 if (!m_IoGate.tryAcquire()) {
396 if ((m_pBase->
read8(4) & 1) == 0) {
400 const uint8_t result = m_pBase->
read8();
406 if (m_DebugState.active()) {
407 byte = readByteNonBlock();
411 const size_t readMode = m_ReadMode.value();
412 if (readMode == StoppingReadMode) {
415 if (readMode == PollingReadMode) {
420 size_t numRead = m_FirstPortBuffer.
read(&
byte, 1, block);
425 if (m_DebugState.active()) {
426 byte = readByteNonBlock();
430 const size_t readMode = m_ReadMode.value();
431 if (readMode == StoppingReadMode) {
434 if (readMode == PollingReadMode) {
439 size_t numRead = m_SecondPortBuffer.
read(&
byte, 1, block);
444 m_DebugState.set(debugState);
453 bool received =
false;
454 while (m_ReadMode.value() != StoppingReadMode && !m_DebugState.active()) {
455 if (!acquireIoForThread()) {
456 return IrqDisposition::NotHandled;
458 const uint8_t status = m_pBase->
read8(4);
459 if (!(status & OutputBufferFull)) {
463 const uint8_t value = m_pBase->
read8(0);
464 const bool secondPort = (status & SecondPortData) != 0;
468 Buffer<uint8_t>& destination = secondPort ? m_SecondPortBuffer : m_FirstPortBuffer;
473 m_SecondPortDrops += 1;
475 m_FirstPortDrops += 1;
479 if (++batch == 256) {
490 : IrqDisposition::Handled;
493bool Ps2Controller::waitForReadingLocked() {
495 for (
size_t poll = 0; poll < Ps2IoPollLimit; ++poll) {
496 if (m_pBase->
read8(4) & OutputBufferFull)
500 if (m_pBase->
read8(4) & OutputBufferFull)
502 ERROR(
"PS/2 controller output buffer did not fill before timeout");
506bool Ps2Controller::waitForWritingLocked() {
508 for (
size_t poll = 0; poll < Ps2IoPollLimit; ++poll) {
509 if (!(m_pBase->
read8(4) & InputBufferFull))
513 if (!(m_pBase->
read8(4) & InputBufferFull))
515 ERROR(
"PS/2 controller input buffer did not empty before timeout");
size_t writeAvailable(const T *buffer, size_t count, bool atomic=false)
size_t read(T *buffer, size_t count, bool block=true)
virtual Vector< Address * > & addresses()
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 bool inDeviceHardIrq()
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()