Some thoughts on Zephyr Console API


Paul Sokolovsky
 

Hello,

I would like to share some thoughts/woes with using Zephyr console API,
based on the experience with (initial) porting of two console
application to Zephyr, both are scripting languages, JerryScript and
MicroPython. As those are scripting languages, interactive prompt (aka
REPL) play important role for them.

Both JrS and uPy are intended for embedded usage, and both share a
similar architecture, where there's "unix" port, which can serve as a
demo, and as a starting port for a port to specific RTOS/hardware. Both
of these projects for reading a console input require classical
K&R-style getchar()-like function, with a semantics of "read a single
char from console, block if not available". Alternatively, they can
interpret a whole line at once, that's classical gets() with semantics
of "block until whole line from console is available, then return it".
Using gets()-like call has a benefit that line-editing capabilities can
be provided, and transparent to an application.

Let's see what effort was required to port them to Zephyr:

1. JerryScript port was originally done by Sergio Rodriguez (Zephyr
team member or at least member of another team in Intel which uses
Zephyr closely). Instead of pull-style gets(), it uses push-style
whole line delivery via callback from Zephyr shell subsystem. Works
well, and there's line editing, completion support offered by Zephyr
shell, which is really nice. And while not as easy as a single gets(),
set up of it is pretty easy and high-level. And it works nice, until
you try to enter a line with at least 10 spaces, e.g.:

for (var i = 0; i < 10000; i++) { a.push("foo"); }

That ends up with "Too many parameters (max 10)" error. That's because
shell unconditionally tokenizes an input lines into space-separate
tokens, and the default (non-configurable) limit is 10. And of course,
JerryScript then needs to join them back.

2. With MicroPython, I decided to follow "getchar" approach, especially
that uPy has an own readline-like implementation with more capabilities
then Zephyr. So, I started to look how to make Zephyr pass me input
chars. I saw drivers/console/uart_pipe.c, and thought that's the best
approach, as it allows the leanest way to do that. Unfortunately,
playing with for few hours, I couldn't make it work. I then followed
another lead - using uart_irq_input_hook. With some extra effort I was
able to make it work, and created a standalone module which just
exposes zephyr_getchar() function:
https://github.com/pfalcon/zephyr_getchar


Conclusions:

Getting console input with Zephyr is *HARD*. I originally reported
an issue #1 above as https://jira.zephyrproject.org/browse/ZEP-532 ,
and suggested that I could fork console_handler_shell.c, remove
tokenizing and would get what I want. But arguably, that's a lot of
work to just gets() which is just there on a lot of other systems.
However, with "getchar" trial, I essentially did just that - coded
simple, but not exactly trivial code on top of what Zephyr.

So, I guess I could do similar for "gets" too, but one problem is that
it unlikely to help many other people - it really would be nice to have
that in the main Zephyr repo, and engineered properly (for example,
zephyr_getchar module above isn't compatible with Zephyr shell).

Would that make sense for Zephyr? Requirements would be simple:

1. Add ability to receive a single char from a console; also, a single
line (with editing and other niceties).

2. Push-style interface with a callback is probably OK, but having
pull-style interface would make porting a lot of software easier. And
with a push-style, a multiple consumers problem immediately manifests
itself.


Thanks,
Paul

Linaro.org | Open source software for ARM SoCs
Follow Linaro: http://www.facebook.com/pages/Linaro
http://twitter.com/#!/linaroorg - http://www.linaro.org/linaro-blog

Join devel@lists.zephyrproject.org to automatically receive all group messages.