Skip to content

Commit

Permalink
A lot of updates to the SMC code (#2)
Browse files Browse the repository at this point in the history
* Use Stefan's compact version of OneButton.

* Enable Perixx keyboard; set numlock at power on

* Remove activity LED debug code.

* Move mouse and keyboard initialization state machine to START_RESET after reset.

* Implement buffer full handling

* Removed some serial print debugging

* remove serial debugging

* Fixed bug in process byte function

* Removed strange line from count()

* Commented out lastBitMillis as was done in upstream code

* Bugfix in putModifiers function

* Added support in the scancode state machine for the two byte response to the read ID command

* Forgot break statements

* Fixed faulty check for start of read ID response start

* Changed bit receive timeout not to clear the whole buffer

* Fixed modifier key tracking during buffer full

* Improvements to handling of some corner cases + general code cleanup

* Fixed a typo, volatile instead of virtual

* Fixed typo

* Fixing bugs in the keyboard buffer full handling

* Reset mouse PS2 state machine when powering off.

* Implemented keyboard buffer full handling

* Break/Pause key to hold NMI pin

* Activate NMI button

* Bound NMI to Restore (=PrtScr)

* Searching for a NMI problem

* Adjusted KB state machine to be aware of Num Lock decorator

* Handle scan code differences when Ctrl and Alt are pressed

* Trying to extend kbd state machine to catch all PS/2 scancode type 2 weirdness

* Simplification of the scancode state machine

* Fix to ensure that putModifiers exits with correct scancode_state value

* Fixed bug in scancode state machine

* Fix behavior when resuming after buffer overrun; would previously lose first key thereafter

* Fixed bug regarding two byte read ID response

* Check RESB polarity

* Code cleanup

---------

Co-authored-by: jburks <joe@burks-family.us>
  • Loading branch information
stefan-b-jakobsson and jburks authored Feb 17, 2023
1 parent a4d2c75 commit 9cbd5a9
Show file tree
Hide file tree
Showing 5 changed files with 583 additions and 93 deletions.
187 changes: 154 additions & 33 deletions mouse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,30 @@
#include "smc_pins.h"

extern bool SYSTEM_POWERED;
extern PS2Port<PS2_KBD_CLK, PS2_KBD_DAT, 16> Keyboard;
extern PS2KeyboardPort<PS2_KBD_CLK, PS2_KBD_DAT, 16> Keyboard;
extern PS2Port<PS2_MSE_CLK, PS2_MSE_DAT, 8> Mouse;

MOUSE_INIT_STATE_T mouse_init_state = OFF;

// Mouse initialization state machine
void MouseInitTick()
void MouseTick()
{
static bool watchdog_armed = false;
static uint16_t watchdog_timer = 1023;
static MOUSE_INIT_STATE watchdog_expire_state = OFF;

MOUSE_INIT_STATE_T next_state = mouse_init_state;

if (mouse_init_state != OFF && SYSTEM_POWERED == 0)
{
mouse_init_state = OFF;
watchdog_armed = false;
watchdog_timer = 1023;
Mouse.reset();
return;
}
if (watchdog_armed) {
if (watchdog_timer == 0) {
mouse_init_state = watchdog_expire_state;
next_state = watchdog_expire_state;
watchdog_armed = false;
}
else {
Expand All @@ -41,10 +43,10 @@ void MouseInitTick()
{
case OFF:
if (SYSTEM_POWERED != 0) {
mouse_init_state = POWERUP_BAT_WAIT;
next_state = POWERUP_BAT_WAIT;

// If we don't see the mouse respond, jump to sending it a reset.
MOUSE_WATCHDOG(START_RESET);
MOUSE_REARM_WATCHDOG();
}
break;

Expand All @@ -53,11 +55,12 @@ void MouseInitTick()
uint8_t b = Mouse.next();
if (b == BAT_OK)
{
mouse_init_state = POWERUP_ID_WAIT;
MOUSE_WATCHDOG(START_RESET); // If we don't see the mouse respond, jump to sending it a reset.
next_state = POWERUP_ID_WAIT;
MOUSE_REARM_WATCHDOG();
} else if (b == BAT_FAIL) {
mouse_init_state = FAILED;
next_state = FAILED;
}
// Let watchdog send us to START_RESET if we don't get BAT
}

break;
Expand All @@ -68,31 +71,31 @@ void MouseInitTick()
if (b == MOUSE_ID)
{
Mouse.sendPS2Command(mouse_command::SET_SAMPLE_RATE);
MOUSE_WATCHDOG(START_RESET);
mouse_init_state = SAMPLERATECMD_ACK_WAIT;
MOUSE_REARM_WATCHDOG();
next_state = SAMPLERATECMD_ACK_WAIT;
}
// Watchdog will eventually send us to START_RESET if we don't get MOUSE_ID
}
break;

case PRE_RESET:
mouse_init_state = START_RESET;
next_state = START_RESET;
break;

case START_RESET:
Mouse.flush();
Mouse.sendPS2Command(mouse_command::RESET);
MOUSE_WATCHDOG(FAILED);
mouse_init_state = RESET_ACK_WAIT;
next_state = RESET_ACK_WAIT;
break;

case RESET_ACK_WAIT:
if (mstatus == mouse_command::ACK) {
Mouse.next();
MOUSE_WATCHDOG(START_RESET);
mouse_init_state = RESET_BAT_WAIT;
MOUSE_REARM_WATCHDOG();
next_state = RESET_BAT_WAIT;
} else {
mouse_init_state = FAILED; // Assume an error of some sort.
next_state = FAILED; // Assume an error of some sort.
}
break;

Expand All @@ -101,10 +104,10 @@ void MouseInitTick()
if (Mouse.available()) {
uint8_t b = Mouse.next();
if ( b != mouse_command::BAT_OK ) {
mouse_init_state = FAILED;
next_state = FAILED;
} else {
MOUSE_WATCHDOG(START_RESET);
mouse_init_state = RESET_ID_WAIT;
MOUSE_REARM_WATCHDOG();
next_state = RESET_ID_WAIT;
}
}
break;
Expand All @@ -114,57 +117,175 @@ void MouseInitTick()
if (Mouse.available()) {
uint8_t b = Mouse.next();
if ( b != mouse_command::MOUSE_ID ) {
mouse_init_state = FAILED;
next_state = FAILED;
} else {
Mouse.sendPS2Command(mouse_command::SET_SAMPLE_RATE);
MOUSE_WATCHDOG(START_RESET);
mouse_init_state = SAMPLERATECMD_ACK_WAIT;
MOUSE_REARM_WATCHDOG();
next_state = SAMPLERATECMD_ACK_WAIT;
}
}
break;

case SAMPLERATECMD_ACK_WAIT: // RECEIVE ACK, SEND 20 updates/sec
Mouse.next();
if (mstatus != mouse_command::ACK)
mouse_init_state = PRE_RESET; // ?? Try resetting again, I guess.
next_state = PRE_RESET; // ?? Try resetting again, I guess.
else {
Mouse.sendPS2Command(60);
MOUSE_WATCHDOG(START_RESET);
mouse_init_state = SAMPLERATE_ACK_WAIT;
MOUSE_REARM_WATCHDOG();
next_state = SAMPLERATE_ACK_WAIT;
}
break;

case SAMPLERATE_ACK_WAIT: // RECEIVE ACK, SEND ENABLE
Mouse.next();
if (mstatus != mouse_command::ACK)
mouse_init_state = PRE_RESET;
next_state = PRE_RESET;
else {
Mouse.sendPS2Command(mouse_command::ENABLE);
MOUSE_WATCHDOG(START_RESET);
mouse_init_state = ENABLE_ACK_WAIT;
MOUSE_REARM_WATCHDOG();
next_state = ENABLE_ACK_WAIT;
}
break;

case ENABLE_ACK_WAIT: // Receive ACK
Mouse.next();
if (mstatus != mouse_command::ACK) {
mouse_init_state = PRE_RESET;
next_state = PRE_RESET;
} else {
mouse_init_state = MOUSE_INIT_DONE;
MOUSE_WATCHDOG_DISARM();
next_state = MOUSE_INIT_DONE;
MOUSE_DISARM_WATCHDOG();
}
break;

case MOUSE_INIT_DONE:
// done
MOUSE_WATCHDOG_DISARM();
mouse_init_state = MOUSE_READY;
MOUSE_DISARM_WATCHDOG();
next_state = MOUSE_READY;
break;

case MOUSE_READY:
break;

default:
// This is where the FAILED state will end up
break;
}
mouse_init_state = next_state;
}

uint8_t kbd_init_state = 0;
uint8_t kbd_status_leds = 0;

void KeyboardTick()
{
static bool watchdog_armed = false;
static uint16_t watchdog_timer = 255;
static uint8_t watchdog_expire_state = OFF;
uint8_t next_state = kbd_init_state;
static uint8_t blink_counter = 0;
blink_counter++;

if (kbd_init_state != OFF && SYSTEM_POWERED == 0)
{
kbd_init_state = OFF;
watchdog_armed = false;
watchdog_timer = 255;
return;
}
if (watchdog_armed) {
if (watchdog_timer == 0) {
next_state = watchdog_expire_state;
watchdog_armed = false;
}
else {
watchdog_timer--;
}
}

PS2_CMD_STATUS kstatus = Keyboard.getCommandStatus();
if (kstatus == PS2_CMD_STATUS::CMD_PENDING)
{
return;
}

switch(kbd_init_state)
{
case OFF:
if (SYSTEM_POWERED != 0) {
next_state = POWERUP_BAT_WAIT;

// If we don't see the mouse respond, jump to sending it a reset.
MOUSE_REARM_WATCHDOG();
}
break;

case POWERUP_BAT_WAIT:
if (Keyboard.available()) {
uint8_t b = Keyboard.next();
if (b == BAT_OK)
{
kbd_status_leds = 0x2;
next_state = KBD_SET_LEDS1;
MOUSE_REARM_WATCHDOG();
} else if (b == BAT_FAIL) {
next_state = FAILED;
}
// Let watchdog send us to START_RESET if we don't get BAT
}
break;

case START_RESET:
Keyboard.flush();
Keyboard.sendPS2Command(mouse_command::RESET);
MOUSE_WATCHDOG(FAILED);
next_state = RESET_ACK_WAIT;
break;

case RESET_ACK_WAIT:
if (kstatus == mouse_command::ACK) {
Keyboard.next();
MOUSE_REARM_WATCHDOG();
next_state = POWERUP_BAT_WAIT;
} else {
next_state = FAILED; // Assume an error of some sort.
}
break;


case KBD_SET_LEDS1:
Keyboard.sendPS2Command(SET_STATUS_INDICATORS);
next_state = SET_LEDS1_ACK_WAIT;
MOUSE_REARM_WATCHDOG();
break;

case SET_LEDS1_ACK_WAIT:
Keyboard.next();
if (kstatus != PS2_CMD_STATUS::CMD_ACK) {
next_state = FAILED;
} else {
next_state = SET_LEDS2_ACK_WAIT;
Keyboard.sendPS2Command(kbd_status_leds);
MOUSE_REARM_WATCHDOG();
}
break;

case SET_LEDS2_ACK_WAIT:
Keyboard.next();
if (kstatus != PS2_CMD_STATUS::CMD_ACK) {
next_state = FAILED;
} else {
next_state = KBD_READY;
MOUSE_DISARM_WATCHDOG();
}
break;

case KBD_READY:
break;

default:
// This is where the FAILED state will end up
break;
}
kbd_init_state = next_state;
}
33 changes: 24 additions & 9 deletions mouse.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@

enum mouse_command : uint8_t
{
RESET = 0xFF,
ACK = 0xFA,
BAT_OK = 0xAA,
BAT_FAIL = 0xFC,
MOUSE_ID = 0x00,
SET_SAMPLE_RATE = 0xF3,
READ_DEVICE_TYPE = 0xF2,
RESET = 0xFF, // Reset device
ACK = 0xFA, // Acknowledge
BAT_OK = 0xAA, // Basic Acceptance Test passed
BAT_FAIL = 0xFC, // Basic Acceptance Test failed
MOUSE_ID = 0x00, // Mouse identifier
SET_SAMPLE_RATE = 0xF3, // Mouse sample rate command
READ_DEVICE_TYPE = 0xF2, // Request device type
SET_RESOLUTION = 0xE8,
SET_SCALING = 0xE6,
SET_STATUS_INDICATORS = 0xED, // Set status indicators (keyboard LEDs)
ENABLE = 0xF4
};

Expand All @@ -31,16 +32,30 @@ typedef enum MOUSE_INIT_STATE : uint8_t {
ENABLE_ACK_WAIT,
MOUSE_INIT_DONE,
MOUSE_READY,

KBD_SET_LEDS1,
SET_LEDS1_ACK_WAIT,
KBD_SET_LEDS2,
SET_LEDS2_ACK_WAIT,
KBD_READY,

FAILED = 255
} MOUSE_INIT_STATE_T;

extern MOUSE_INIT_STATE_T mouse_init_state;
extern uint8_t kbd_init_state;

#define MOUSE_REARM_WATCHDOG() \
watchdog_armed = true; \
watchdog_timer = 255; \
watchdog_expire_state = (MOUSE_INIT_STATE::START_RESET)

#define MOUSE_WATCHDOG(x) \
watchdog_armed = true; \
watchdog_timer = 1023; \
watchdog_timer = 255; \
watchdog_expire_state = (x)

#define MOUSE_WATCHDOG_DISARM() watchdog_armed = false
#define MOUSE_DISARM_WATCHDOG() watchdog_armed = false

void MouseTick();
void KeyboardTick();
Loading

0 comments on commit 9cbd5a9

Please sign in to comment.