this does mean your device is notably less secure if you're operating
with usb serial turned on, as a host can just inject arbitrary commands
into your device!
verified to address the issue at least in a simple test case
1) drop repeated keys instead of panic-unwrapping
2) reduce key repeat rate to something more sane
In general, hosted mode is assumed "by default" if the target
is not Xous. This allows packages to be built cleanly on command
lines that don't know to pass a --feature flag to specify a target.
However, an explicit "hosted" flag is still retained so that we
can confirm the build system intent. This saves us from the failure
mode that we thought we were building for target A, but accidentally
ommitted the flag, and instead we've been silently building for
"hosted" mode without any warning. The main consumer of the "hosted"
flag is now just the build.rs script inside of the utralib.
This will allow for e.g. xous on riscv32-imac but not a precursor
target, for example: by using feature selectors we get to have
a bigger range of targets that we support, such as for implementations
that haven't been upstreamed or have official target-triples.
The downsides are:
- patch override libraries don't absorb feature flags. so, for
example, the AES crate that is used to patch other crates won't
be aware of the target features without modifying the referencing
crates.
- features are normally meant to be additive, but target features
are mutually exclusive. The mutex is enforced by a check in
utralib that throws a panic on build if two targets are accidentally
selected at the same time.
Adjust the connection number back to 4. There isn't a connection
made for a keyboard backlight server anymore. This is necessary
for root key initialization to proceed.
In general, if an enum has been explicitly numbered, it means
that there are crates that depend on the actual number assigned,
and changing them will break other crates.
This is done to allow for cross-compatibility reasons, for
example, going to a C FFI or to the standard library, where
we can't simply refer to a Rust crate to import the symbolic
enums and have the compiler figure out a consistent numbering
method.
Thus even though it's a bit "ugly", please assign only unused
numbers to new enums, and when old enums are retired, do not
re-use the number.
- Opcodes are mapped to static IDs
- connection ID is now visible to someone who already owns
the keyboard object. This can then be shared with the FFI
interface to do calls to the keyboard server.
This macro will be removed, since it is no longer necessary. Remove this
macro across the project and replace it with a standard `main()`
function.
Signed-off-by: Sean Cross <sean@xobs.io>
remaps keys based on the assumed keymap based on the user's
preference for the keyboard layout on the device.
So far, only dvorak and QWERTY are supported.
tts is working fairly well and much more stably, menus and ui
elements are wired up for tts.
just waiting for the latest braille keyboard pcb to come in and
then we'll be able to fire off some prototypes...
Keyboard setting is "the one and only" exception to user settings
that are saved without encryption. This is because you need your
keyboard layout to be set in order for you to type your password.
Thus, this needed a bit of a special-case to handle its storage record.
We decided to lop off 1 page from the loader and have it store
the keyboard layout code.
Let's hope we can have the discipline to really just store that
one word of information and not later be like "gee wally, you have
a whoooole 4092 unused bytes of space we can cram unencrypted
user settings in, sure would be a waste to not put those use those".
Was doing disjoint loads and stores on an Atomic, really, should
be using the fetch_add, fetch_sub variants to be proper.
Didn't seem to impact anything but maybe there's a future version
of me that high fives me for doing this.
I wrote in a commit yesterday how there was no ROI on doing this
yet somehow I kept on doing it. I blame poor impulse control.
Turns out there was a series of race conditions and bugs that
caused this thing to "mostly work" on the first go at it but
really hard to debug and get very right.
1. Litex UART has a weird undocumented API. You advance the Rx
FIFO by writing `1` to the PENDING bit, and no other way. However,
you determine if there are any more characters to receive by
reading the EMPTY bit and seeing if it is 1. The previous method
wrote `1` to the PENDING bit and then used the PENDING bit to
see if a character was available, under the theory that the
PENDING bit would be set again if there was still a character.
This is somewhat true, except that the PENDING bit is set only
some amount of time later which is about comparable to a few
CPU instructions, so if you had a fast loop path the PENDING
bit would show as cleared and you'd exit, which would cause
no further interrupts to fire because the interrupt was
effecitvely not acknowledged.
This isn't written up anywhere but I figured this out by
just reading the source code for Litex UART and BIOS:
e8be391504/litex/soc/cores/uart.py (L264-L279)ccd3ab17be/litex/soc/software/libbase/uart.c (L38-L44)
2. I had the Key Injector IP block in the `sys` domain, which
would have its clock cut when the CPU went to sleep, which would
cause input characters to disappear when the CPU went into power
saving. moving this to "always on" domain fixes that.
3. I had the UART PHY in the always on block (so you could
send or receive exactly one character), but not the
separate UART block (which contained the FIFOs).
The UART block would also go into power
savings, and this would cause FIFO entries to go missing.
4. Escape sequences get sent as a burst of characters. The Key Injector
originally did not have a FIFO of its own, so it would only
remember the *last* key of the sequence that was written, causing
some keys to be dropped occassionally. Adding a 16-deep FIFO
to the injector fixes this.
5. The code I had to read out the injector's results was designed with
the "strobe" bit (to indicate that the FIFO had characters) in the
same regsiter as the character itself. Thus the loop that 1. checked
if the fifo was empty then 2. read and forwarded the character
would actually perform two reads from the FIFO. However, this "mostly worked"
I think because the FIFO retains the last value that was written
even when it was empty, so if only one character was read at a time
everything was fine. It only didn't work when you stuffed two characters
in there, as you'd drop the first one. Reading the FIFO once and
then interpreting the code with a bitmask fixes this.
6. The escape sequences, in their full glory with no dropped characters,
required a refactored parsing routine, which I have now added.
7. backspace->delete mappings, the bane of computer engineers since 1960.
OK. very bald yak. Here I lay down the shears next to several
mooshi pillows stuffed with the finest Yak hair.
just couldn't let it go. figured out the issue is a race condition
in how the Litex UARTs are constructed...I don't know how Litex
normally handles this kind of situation but they have this funky
construction where reading the fifo doesn't advance it, you have
to...clear the pending bit. I think it was meant to be a "clever"
thing that saves an instruction or something like that since you
always have to clear the pending bit.
Anyways, the previous incarnation of this loop checked to see
if the pending bit was set again and if it was it'd run back
and fetch another character. This turns out to be a bad idea
because the pending bit goes through a multireg and other stuff
before it's readable which means it has a lot of delay on it.
This leads to a race condition.
This version actually checks if the fifo is empty and if it isn't,
it reads the character *and* pumps the pending bit (because
the FIFO doesn't auto-advance when you read it, you have to clear
the pending bit).
This works a lot better, but it's not at all well documented how
this works and it's very hard to tell from reading the source code
because it's written using really fancy migen abstractions that
are also poorly documented and have a lot of cursed constructions.
Anyways. At least the console doesn't die now when you type
a control character, *but* you do have the problem that the
escape codes get mis-interpreted sometimes. If it really really
becomes important later on to fix that it would involve tracing
out the sequence of keys that arrive at InjectKey and figuring
out if things are coming out of order or what is triggering the
bad escape codes.
there is still some mysterious problem involving a race condition
of the raw hardware console UART Rx path deciding it doesn't want
to report new characters anymore to the interrupt handler that
is triggered when multi-byte sequences are shoved down the
pipe. So long as you're typing simple commands it's fine, but
escape sequences tend to lock things up. I don't think there's
enough ROI to continue investigating this facility.