Skip to content

Commit

Permalink
- rewritten ULP code from scratch (experimental)
Browse files Browse the repository at this point in the history
- improved PS/2 keyboard and mouse reliability (no more clashes)
- removed PS2Controller.waitData, injectInRXBuffer, suspend() and resume()
- added PS2Controller.enableRX() and disableRX()
- added PS2Device.syncError(), CLKTimeOutError()
  • Loading branch information
fdivitto committed Mar 11, 2021
1 parent 4a7834d commit a32e008
Show file tree
Hide file tree
Showing 9 changed files with 844 additions and 802 deletions.
1,211 changes: 570 additions & 641 deletions src/comdrivers/ps2controller.cpp

Large diffs are not rendered by default.

73 changes: 26 additions & 47 deletions src/comdrivers/ps2controller.h
Expand Up @@ -33,6 +33,7 @@

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"

#include "fabutils.h"
#include "fabglconf.h"
Expand Down Expand Up @@ -114,15 +115,13 @@ class PS2Controller {
void begin(PS2Preset preset = PS2Preset::KeyboardPort0_MousePort1, KbdMode keyboardMode = KbdMode::CreateVirtualKeysQueue);

/**
* @brief Gets the number of scancodes available in the controller buffer.
* @brief Determines if one byte has been received from the specified port
*
* @param PS2Port PS2 port number (0 = port 0, 1 = port1).
*
* @return The number of scancodes available to read.
* @return True if one byte is available
*/
int dataAvailable(int PS2Port);

bool waitData(int timeOutMS, int PS2Port);
bool dataAvailable(int PS2Port);

/**
* @brief Gets a scancode from the queue.
Expand All @@ -131,7 +130,7 @@ class PS2Controller {
*
* @return The first scancode of the queue (-1 if no data is available).
*/
int getData(int PS2Port);
int getData(int PS2Port, int timeOutMS);

/**
* @brief Sends a command to the device.
Expand All @@ -142,42 +141,22 @@ class PS2Controller {
void sendData(uint8_t data, int PS2Port);

/**
* @brief Injects a byte into the RX buffer.
*
* Injects a byte as if it were actually sent by the device.
*
* @param value Byte to inject.
* @param PS2Port PS2 port number (0 = port 0, 1 = port1).
*/
void injectInRXBuffer(int value, int PS2Port);

/**
* @brief Suspends PS/2 ports operations
*/
void suspend();

/**
* @brief Resumes PS/2 ports operations
*/
void resume();

/**
* @brief Suspends PS/2 port driving the CLK line Low
* @brief Disables inputs from PS/2 port driving the CLK line Low
*
* Use resumePort() to release CLK line.
* Use enableRX() to release CLK line.
*
* @param PS2Port PS2 port number (0 = port 0, 1 = port1).
*/
void suspendPort(int PS2Port);
void disableRX(int PS2Port);

/**
* @brief Resumes PS/2 port releasing CLK line
* @brief Enables inputs from PS/2 port releasing CLK line
*
* Use suspendPort() to suspend.
* Use disableRX() to disable inputs from PS/2 port.
*
* @param PS2Port PS2 port number (0 = port 0, 1 = port1).
*/
void resumePort(int PSPort);
void enableRX(int PSPort);

/**
* @brief Returns the instance of Keyboard object automatically created by PS2Controller.
Expand All @@ -202,15 +181,18 @@ class PS2Controller {
*
* @return A pointer to PS2Controller singleton object
*/
static PS2Controller * instance() { return s_instance; }
static PS2Controller * instance() { return s_instance; }

bool parityError(int PS2Port) { return m_parityError[PS2Port]; }

void warmInit();
bool syncError(int PS2Port) { return m_syncError[PS2Port]; }

bool parityError(int PS2Port) { return m_parityError[PS2Port]; }
bool CLKTimeOutError(int PS2Port) { return m_CLKTimeOutError[PS2Port]; }

private:

static void IRAM_ATTR rtc_isr(void * arg);

static void IRAM_ATTR ULPWakeISR(void * arg);

static PS2Controller * s_instance;

Expand All @@ -219,21 +201,18 @@ class PS2Controller {
Keyboard * m_keyboard;
Mouse * m_mouse;

// address of next word to read in the circular buffer
volatile int m_readPos[2];

// task that is waiting for TX ends
volatile TaskHandle_t m_TXWaitTask[2];
bool m_portEnabled[2];

// task that is waiting for RX event
volatile TaskHandle_t m_RXWaitTask[2];
intr_handle_t m_ULPWakeISRHandle;

intr_handle_t m_isrHandle;
// true if last call to getData() had a parity, sync error (start or stop missing bits) or CLK timeout
bool m_parityError[2];
bool m_syncError[2];
bool m_CLKTimeOutError[2];

int16_t m_suspendCount; // 0 = not suspended, >0 suspended
// one word queue (contains just the last received word)
QueueHandle_t m_dataIn[2];

// true if last call to getData() had a parity error
bool m_parityError[2];
};


Expand Down
54 changes: 34 additions & 20 deletions src/comdrivers/ps2device.cpp
Expand Up @@ -120,45 +120,62 @@ bool PS2Device::parityError()
}


bool PS2Device::syncError()
{
return PS2Controller::instance()->syncError(m_PS2Port);
}


bool PS2Device::CLKTimeOutError()
{
return PS2Controller::instance()->CLKTimeOutError(m_PS2Port);
}


void PS2Device::suspendPort()
{
PS2Controller::instance()->suspendPort(m_PS2Port);
PS2Controller::instance()->disableRX(m_PS2Port);
}


void PS2Device::resumePort()
{
PS2Controller::instance()->resumePort(m_PS2Port);
PS2Controller::instance()->enableRX(m_PS2Port);
}


int PS2Device::getData(int timeOutMS)
{
TimeOut timeout;
constexpr int INTER_GETDATA_TIMEOUT_MS = 100;
constexpr int INTER_GETDATA_PAUSE_MS = 10;

int interTimeOut = timeOutMS > -1 ? imin(timeOutMS, INTER_GETDATA_TIMEOUT_MS) : INTER_GETDATA_TIMEOUT_MS;

int ret = -1;
while (!timeout.expired(timeOutMS)) {
TimeOut timeout;
while (true) {
lock(-1);
ret = PS2Controller::instance()->getData(m_PS2Port);
ret = PS2Controller::instance()->getData(m_PS2Port, interTimeOut);
unlock();
if (ret > -1 || parityError())
if (ret > -1 || parityError() || syncError() || CLKTimeOutError() || timeout.expired(timeOutMS))
break;
lock(-1);
PS2Controller::instance()->waitData((timeOutMS > -1 ? timeOutMS : m_cmdSubTimeOut), m_PS2Port);
unlock();
vTaskDelay(10 / portTICK_PERIOD_MS);
// give the opportunity for other sends
vTaskDelay(INTER_GETDATA_PAUSE_MS / portTICK_PERIOD_MS);
}
return ret;
}


bool PS2Device::sendCommand(uint8_t cmd, uint8_t expectedReply)
{
for (int i = 0; i < m_retryCount; ++i) {
PS2Controller::instance()->sendData(cmd, m_PS2Port);
if (getData(m_cmdTimeOut) != expectedReply)
continue;
return true;
}
constexpr int INTER_WAITREPLY_TIMEOUT_MS = 10;

PS2Controller::instance()->sendData(cmd, m_PS2Port);
TimeOut timeout;
do {
if (getData(INTER_WAITREPLY_TIMEOUT_MS) == expectedReply)
return true;
} while (!timeout.expired(m_cmdTimeOut));
return false;
}

Expand All @@ -178,10 +195,7 @@ void PS2Device::requestToResendLastByte()
bool PS2Device::send_cmdLEDs(bool numLock, bool capsLock, bool scrollLock)
{
PS2DeviceLock deviceLock(this);
if (!sendCommand(PS2_CMD_SETLEDS, PS2_REPLY_ACK))
return false;
bool ret = sendCommand((scrollLock << 0) | (numLock << 1) | (capsLock << 2), PS2_REPLY_ACK);
return ret;
return sendCommand(PS2_CMD_SETLEDS, PS2_REPLY_ACK) && sendCommand((scrollLock << 0) | (numLock << 1) | (capsLock << 2), PS2_REPLY_ACK);
}


Expand Down
4 changes: 4 additions & 0 deletions src/comdrivers/ps2device.h
Expand Up @@ -90,6 +90,10 @@ class PS2Device {

bool parityError();

bool syncError();

bool CLKTimeOutError();

/**
* @brief Sends a raw command to the PS/2 device and wait for reply
*
Expand Down
4 changes: 4 additions & 0 deletions src/devdrivers/keyboard.cpp
Expand Up @@ -167,6 +167,10 @@ int Keyboard::getNextScancode(int timeOutMS, bool requestResendOnTimeOut)
{
while (true) {
int r = getData(timeOutMS);
if (r == -1 && CLKTimeOutError()) {
// try to recover a stall sending a re-enable scanning command
send_cmdEnableScanning();
}
if (r == -1 && requestResendOnTimeOut) {
requestToResendLastByte();
continue;
Expand Down

0 comments on commit a32e008

Please sign in to comment.