Skip to content

Fix the Shift & Symbol key issue on GPIO-based keyboards#37

Open
tslmy wants to merge 2 commits into
brain-hackers:brain-rebase-6.1from
tslmy:fix-sticky-mod-key
Open

Fix the Shift & Symbol key issue on GPIO-based keyboards#37
tslmy wants to merge 2 commits into
brain-hackers:brain-rebase-6.1from
tslmy:fix-sticky-mod-key

Conversation

@tslmy

@tslmy tslmy commented Jun 14, 2026

Copy link
Copy Markdown

Disclaimer: I used AI heavily in this fix. I at least ensured that I understood everything it wrote.

Background

For those who came across this PR in the future, here's the context.

Symptom

As noted by Nasubi on Discord (machine-translated):

On my Brain device:
When I try to enter a "." by pressing the "Symbol" key and "M" simultaneously, the "." repeats continuously and won't stop until I press another key.

Also, I just checked on my new Brain unit:
When entering ",", ".", or "/", a repeat input almost always occurs.
Regarding inputs other than ",", ".", or "/", the issue also occurred with "4" and "=".
Other keys seem fine based on my current testing.

(It seems to happen with those specific keys even when pressed simultaneously with Shift—specifically the E, F, N, M, and - keys.)

As written in this blog post:

I'm testing this on the PW-SH3, and sometimes the simultaneous key input for certain symbols (",", ".", "/") doesn't work right; even when the input registers, it often starts repeating endlessly for some reason.
I'm not sure if other models have the same issue.
In these cases, I deal with it by deleting the extra characters, but the only workaround I could think of to stop the repeating input was to immediately press another key.
(For example, to enter ".", I might press Symbol+M followed by M—resulting in ".m"—and then delete the "m".)
Since "." is frequently used for entering IP addresses and URLs, this is a bit frustrating.
Sometimes the input works fine, but other times it gets recognized as a repeating input, and I haven't been able to control it properly so far.

Context

Here's the actual Brain Row 4 (called "bank 0x10" in the code), from the device tree:

Row 4:  Col0=Shift   Col1=R   Col2=F   Col3=Symbol   Col4=N   Col5=M   Col6=Minus

Both modifiers -- Shift AND Symbol -- physically live on Row 4, together with N, M, and Minus.

Root cause

The original code drove columns like this (simplified):

for each column c:
    drive column c HIGH      ← "activate"
    (all other columns stay driven LOW)
    read the rows
    drive column c LOW again

And the rows were read as: pressed = HIGH. The key idea the original relied on: when you drive column c HIGH, a pressed key on column c pulls its row HIGH, so you see it.

The problem: the other columns were driven LOW the whole time. They are actively forced to ground, not disconnected.

image

The fix

Two coordinated changes.

Change A: idle columns go hi-Z instead of LOW

New scan:

for each column c:
    drive column c LOW          ← "activate" (note: LOW now, see below)
    set all other columns to INPUT mode  ← hi-Z = disconnected!
    read the rows
    set column c back to INPUT mode (hi-Z)

Change B: rows get pull-ups, and we flip the polarity

If idle columns are now disconnected, nothing holds a row wire at a defined value when none of its keys is pressed. It would float. On the device, it would appear as if all keys are dead.

We need to give those wires a "default value". We do this by adding pull-up resistors to the row wires. Now:

  • Key not pressed → row floats up to HIGH via its pull-up → reads 1.
  • Key pressed → connects row to the scanned column, which we drive LOW → strong path to ground beats the weak pull-up → reads 0.

So the logic flips to active-low: 0 now means pressed. (Before, with the drive-HIGH scheme, 1 meant pressed.)

That's why the active column is now driven LOW instead of HIGH: the pressed key needs to pull its pulled-up row down to be detected.

We had to flip the polarity because the i.MX28 has no pull-down resistors on these pins. Eviences:

  1. The binding doc (fsl,mxs-pinctrl.txt) says, "The configuration on the pins includes drive strength, voltage and pull-up." No "pull-downs".

  2. The constants in mxs-pinfunc.h only has two values, and notice they describe an on/off switch, not a direction:

#define MXS_PULL_DISABLE	0
#define MXS_PULL_ENABLE		1

So we can only weakly pull up, not weekly pull down. So the default value can only be "1", not "0". Thus we need to make "0" represent the "pressed" state.

tslmy added 2 commits June 11, 2026 23:15
…te + pull-ups

Symptom: Shift/Symbol chords with same-row keys (N/M/-) unreliable or dropped.

Root cause: Matrix scan drove non-active columns to LOW (push-pull) instead of
hi-Z. Two keys on the same input row but different columns created a short:
one column driven HIGH (scanning), other column driven LOW (idle), both connected
via their pressed keys to the shared row wire. The row voltage became
indeterminate, masking one or both keys.

Fix in two coordinated parts:

1. Device tree (imx28-brain.dtsi): Enable pull-up resistors on the 8 sense/row
   lines (GPIO4_0..7 / ENET0 pins). Pulled-up rows idle at HIGH.

2. Driver (brain-kbd-gpio.c):
   - Scan: Drive only the active column LOW; set all other columns to input mode
     (hi-Z). This eliminates the short.
   - Decode: Remove the leading bitwise NOT (~) to match the new active-low
     polarity (pressed key pulls row LOW, reads as 0).

Result: Same-row chords now work. All keys remain fully functional.

Tested on: PW-SH3 (pwsh3) GPIO model.
…hords to work on the row where "Symbol" key sits.

If we hold a chord like "Sym + N", where both keys are in the same bank, and let's say we release N but keep holding Sym. We should report N immediately, not waiting for the whole bank, because Sym is still making the bank active.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant