Raw keyboard input - keyboard

What I would like to do is to have e.g. a spare raspberry pi with four USB keyboards attached (or four mice).
What I want is to know, not just if a key is pressed, but which keyboard it was pressed on, so that different keyboard's buttons can do different things. (Think macro buttons.)
Likewise if I have four mice plugged in, I want to receive raw data and to be able to distinguish which mouse moved.
Ideally I'd like to know how to do this in C.
The device with the devices plugged into it will be controlled by SSH, so there is no need for keypresses to do what they normally do.

I've made a start. For Linux, and this will suffice for now, there are devices in /dev/input called e.g. /dev/input/event0 from which you can read. Some of these will correspond to actual input devices, and some will be virtual.
Information can be found in /sys/class/input.
For example for my USB keyboard:
$ cat /sys/class/input/input24/name
Logitech K360
Also /sys/class/input/input24/uevent has a lot of useful information.
Now to read raw input from a keyboard, you read from something like /dev/input/event0. To find out what these events are, for example for /dev/input/event0, then /sys/class/input/event0/device points to the corresponding input24 above.
Now to read, in C,
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
/*
struct input_event {
struct timeval time;
__u16 type;
__u16 code;
__s32 value;
};
*/
int main() {
int fd = open("/dev/input/event0", O_RDONLY);
struct input_event evt;
while(1) {
read(fd,&evt,sizeof(evt));
printf("Event: type=%hd code=%hd value=%08x=%d\n",evt.type,evt.code,evt.value,evt.value);
}
}
The only remaining issue I have is how to 'disconnect' a keyboard so that its events are ignored (aside from processes reading raw input). I know how to xinput float 34 to float e.g. device 34. That that is for Xorg, not the text console on a device without X, nor f

I have a solution for keyboards. It's also resistent to unplugging/plugging-in the devices.
save_keys.py:
import keyboard
import requests
from threading import Thread
import subprocess
import time
import sys
import os
if not os.path.exists("/tmp/pressed_keys"):
with open("/tmp/pressed_keys", 'a+') as f:
f.write('somedevice:somekey') #this line has no sence, just creating the file
def keyPressed(keyboardEvent: keyboard.KeyboardEvent):
key = keyboardEvent.name
print(key)
device = keyboardEvent.device
print(device)
with open("/tmp/pressed_keys", 'a+') as f:
f.write(f"{device}:{key)
def getDevices():
return subprocess.getoutput('ls /dev/input/by-id/')
def startNewDeviceListener():
while True:
print("checking devices")
newDevices = getDevices()
print("got devices: ",newDevices)
if (devices != newDevices):
print('Device changes detected, exiting')
sys.exit(1)
time.sleep(0.5)
keyboard.on_press(keyPressed, True)
devices = getDevices()
t = Thread(target=startNewDeviceListener)
t.start()
read_keys.py:
import os
pipe_path = "/tmp/pressed_keys"
pipe_fd = os.open(pipe_path, os.O_RDONLY | os.O_NONBLOCK)
with os.fdopen(pipe_fd) as pipe:
pipe.read() #seek to the end of the file
while True: #read new keys
device_key = pipe.read()
device, key = device_key.split(':')
print(f"got {key} key from {device} device")
save_keys.sh:
#!/bin/bash
while :
do
sudo python3 save_keys.py
sleep 1
done
run.sh:
#!/bin/bash
nohup ./save_keys.sh < /dev/null 2>&1 > /dev/null &
python3 read_keys.py
And finally run everything via:
sudo sh run.sh

Related

C extensions - how to redirect printf to a python logger?

I have a simple C-extension(see example below) that sometimes prints using the printf function.
I'm looking for a way to wrap the calls to the function from that C-extensions so that all those printfs will be redirected to my python logger.
hello.c:
#include <Python.h>
static PyObject* hello(PyObject* self)
{
printf("example print from a C code\n");
return Py_BuildValue("");
}
static char helloworld_docs[] =
"helloworld(): Any message you want to put here!!\n";
static PyMethodDef helloworld_funcs[] = {
{"hello", (PyCFunction)hello,
METH_NOARGS, helloworld_docs},
{NULL}
};
static struct PyModuleDef cModPyDem =
{
PyModuleDef_HEAD_INIT,
"helloworld",
"Extension module example!",
-1,
helloworld_funcs
};
PyMODINIT_FUNC PyInit_helloworld(void)
{
return PyModule_Create(&cModPyDem);
};
setup.py:
from distutils.core import setup, Extension
setup(name = 'helloworld', version = '1.0', \
ext_modules = [Extension('helloworld', ['hello.c'])])
to use first run
python3 setup.py install
and then:
import helloworld
helloworld.hello()
I want to be able to do something like this:
with redirect_to_logger(my_logger)
helloworld.hello()
EDIT: I saw a number of posts showing how to silence the prints from C, but I wasn't able to figure out from it how can I capture the prints in python instead.
Example of such post: Redirect stdout from python for C calls
I assume that this question didn't get much traction because I maybe ask too much, so I don't care about logging anymore... how can I capture the C prints in python? to a list or whatever.
EDIT
So I was able to achieve somewhat a working code that does what I want - redirect c printf to python logger:
import select
import threading
import time
import logging
import re
from contextlib import contextmanager
from wurlitzer import pipes
from helloworld import hello
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
class CPrintsHandler(threading.Thread):
def __init__(self, std, poll_std, err, poll_err, logger):
super(CPrintsHandler, self).__init__()
self.std = std
self.poll_std = poll_std
self.err = err
self.poll_err = poll_err
self.logger = logger
self.stop_event = threading.Event()
def stop(self):
self.stop_event.set()
def run(self):
while not self.stop_event.is_set():
# How can I poll both std and err at the same time?
if self.poll_std.poll(1):
line = self.std.readline()
if line:
self.logger.debug(line.strip())
if self.poll_err.poll(1):
line = self.err.readline()
if line:
self.logger.debug(line.strip())
#contextmanager
def redirect_to_logger(some_logger):
handler = None
try:
with pipes() as (std, err):
poll_std = select.poll()
poll_std.register(std, select.POLLIN)
poll_err = select.poll()
poll_err.register(err, select.POLLIN)
handler = CPrintsHandler(std, poll_std, err, poll_err, some_logger)
handler.start()
yield
finally:
if handler:
time.sleep(0.1) # why do I have to sleep here for the foo prints to finish?
handler.stop()
handler.join()
def foo():
logger.debug('logger print from foo()')
hello()
def main():
with redirect_to_logger(logger):
# I don't want the logs from here to be redirected as well, only printf.
logger.debug('logger print from main()')
foo()
main()
But I have a couple of issues:
The python logs are also being redirected and caught by the CPrintsHandler. Is there a way to avoid that?
The prints are not exactly in the correct order:
python3 redirect_c_example_for_stackoverflow.py
2020-08-18 19:50:47,732 - root - DEBUG - example print from a C code
2020-08-18 19:50:47,733 - root - DEBUG - 2020-08-18 19:50:47,731 - root - DEBUG - logger print from main()
2020-08-18 19:50:47,733 - root - DEBUG - 2020-08-18 19:50:47,731 - root - DEBUG - logger print from foo()
Also, the logger prints all go to err, perhaps the way I poll them causes this order.
I'm not that familiar with select in python and not sure if there is a way to poll both std and err at the same time and print whichever has something first.
On Linux you could use wurlitzer which would capture the output from fprint, e.g.:
from wurlitzer import pipes
with pipes() as (out, err):
helloworld.hello()
out.read()
#'example print from a C code\n'
wurlitzer is based on this article of Eli Bendersky, the code from which you can use if you don't like to depend on third-party libraries.
Sadly, wurlitzer and the code from the article work only for Linux (and possible MacOS).
Here is a prototype (an improved version of the prototype can be installed from my github) for Windows using Eli's approach as Cython-extension (which probably could be translated to ctypes if needed):
%%cython
import io
import os
cdef extern from *:
"""
#include <windows.h>
#include <io.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
int open_temp_file() {
TCHAR lpTempPathBuffer[MAX_PATH+1];//path+NULL
// Gets the temp path env string (no guarantee it's a valid path).
DWORD dwRetVal = GetTempPath(MAX_PATH, // length of the buffer
lpTempPathBuffer); // buffer for path
if(dwRetVal > MAX_PATH || (dwRetVal == 0))
{
return -1;
}
// Generates a temporary file name.
TCHAR szTempFileName[MAX_PATH + 1];//path+NULL
DWORD uRetVal = GetTempFileName(lpTempPathBuffer, // directory for tmp files
TEXT("tmp"), // temp file name prefix
0, // create unique name
szTempFileName); // buffer for name
if (uRetVal == 0)
{
return -1;
}
HANDLE tFile = CreateFile((LPTSTR)szTempFileName, // file name
GENERIC_READ | GENERIC_WRITE, // first we write than we read
0, // do not share
NULL, // default security
CREATE_ALWAYS, // overwrite existing
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, // "temporary" temporary file, see https://learn.microsoft.com/en-us/archive/blogs/larryosterman/its-only-temporary
NULL); // no template
if (tFile == INVALID_HANDLE_VALUE) {
return -1;
}
return _open_osfhandle((intptr_t)tFile, _O_APPEND | _O_TEXT);
}
int replace_stdout(int temp_fileno)
{
fflush(stdout);
int old;
int cstdout = _fileno(stdout);
old = _dup(cstdout); // "old" now refers to "stdout"
if (old == -1)
{
return -1;
}
if (-1 == _dup2(temp_fileno, cstdout))
{
return -1;
}
return old;
}
int restore_stdout(int old_stdout){
fflush(stdout);
// Restore original stdout
int cstdout = _fileno(stdout);
return _dup2(old_stdout, cstdout);
}
void rewind_fd(int fd) {
_lseek(fd, 0L, SEEK_SET);
}
"""
int open_temp_file()
int replace_stdout(int temp_fileno)
int restore_stdout(int old_stdout)
void rewind_fd(int fd)
void close_fd "_close" (int fd)
cdef class CStdOutCapture():
cdef int tmpfile_fd
cdef int old_stdout_fd
def start(self): #start capturing
self.tmpfile_fd = open_temp_file()
self.old_stdout_fd = replace_stdout(self.tmpfile_fd)
def stop(self): # stops capturing, frees resources and returns the content
restore_stdout(self.old_stdout_fd)
rewind_fd(self.tmpfile_fd) # need to read from the beginning
buffer = io.TextIOWrapper(os.fdopen(self.tmpfile_fd, 'rb'))
result = buffer.read()
close_fd(self.tmpfile_fd)
return result
And now:
b = CStdOutCapture()
b.start()
helloworld.hello()
out = b.stop()
print("HERE WE GO:", out)
# HERE WE GO: example print from a C code
This is what I would do if I am free to edit the C code. Open a memory map in C and write to its file descriptor using fprintf(). Expose the file descriptor to Python either as the int and then use mmap module to open it or use os.openfd() to wrap it in a simpler file-like object, or wrap it in file-like object in C and let Python use that.
Then I would create a class that will enable me to write to sys.stdout through usual interface, i.e. its write() method (for Python's side usage) , and that would use select module to poll the file from C that acts as its stdout in a thread. Then I would switch sys.stdout with an object of this class. So, when Python does sys.stdout.write(...) the string will be redirected to sys.stdout.write(), and when the loop in a thread detects output on a file from C, it will write it using sys.stdout.write(). So, everything will be written to the screen and be available to loggers as well.
In this model, the strictly C part will never actually be writing to the file descriptor connected to the terminal.
You can even do much of this in C itself and leave little for the Python's side, but its easier to influence the interpreter from the Python's side as the extension is the shared library which involves some kind of, lets call it, IPC and OS in the whole story. That's why the stdout is not shared between extension and Python in the first place.
If you want to continue printf() on C side, you can see how you can redirect it in C before programming this whole mess.
This answer is strictly theoretical because I have no time to test it; but it should be doable according to my knowledge. If you try it, please let me know in a comment how it went. Perhaps I missed something, but, I am certain the theory is sane.
Beauty of this idea is that it will be OS independent, although the part with shared memory or connecting a file descriptor to allocated space in RAM can be sometimes PITA on Windows.
If you are not constrained to using the printf in C, it would be easier to use the print equivalent from python C API and pass where you want to redirect the message as an argument.
For example, your hello.c would be:
#include <Python.h>
static PyObject* hello(PyObject* self, PyObject *args)
{
PyObject *file = NULL;
if (!PyArg_ParseTuple(args, "O", &file))
return NULL;
PyObject *pystr = PyUnicode_FromString("example print from a C code\n");
PyFile_WriteObject(pystr, file, Py_PRINT_RAW);
return Py_BuildValue("");
}
static char helloworld_docs[] =
"helloworld(): Any message you want to put here!!\n";
static PyMethodDef helloworld_funcs[] = {
{"hello", (PyCFunction)hello,
METH_VARARGS, helloworld_docs},
{NULL}
};
static struct PyModuleDef cModPyDem =
{
PyModuleDef_HEAD_INIT,
"helloworld",
"Extension module example!",
-1,
helloworld_funcs
};
PyMODINIT_FUNC PyInit_helloworld(void)
{
return PyModule_Create(&cModPyDem);
};
We can check if it is working with the program below:
import sys
import helloworld
helloworld.hello(sys.stdout)
helloworld.hello(sys.stdout)
helloworld.hello(sys.stderr)
In the command line we redirect each output separately:
python3 example.py 1> out.txt 2> err.txt
out.txt will have two print calls, while err.txt will have only one, as expected from our python script.
You can check python's print implementation to get some more ideas of what you can do.
cpython print source code

Cygwin FIFO vs native Linux FIFO - discrepancy in blocking behaviour?

The code shown is based on an example using named pipes from some tutorial site
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define FIFO_FILE "MYFIFO"
int main()
{
int fd;
char readbuf[80];
int read_bytes;
// mknod(FIFO_FILE, S_IFIFO|0640, 0);
mkfifo(FIFO_FILE, 0777);
while(1) {
fd = open(FIFO_FILE, O_RDONLY);
read_bytes = read(fd, readbuf, sizeof(readbuf));
readbuf[read_bytes] = '\0';
printf("Received string: \"%s\". Length is %d\n", readbuf, (int)strlen(readbuf));
}
return 0;
}
When executing the server in Windows, using Cygwin, then the server enters an undesired loop, repeating the same message. For example, if you write in a shell:
$ ./server
|
then the "server" waits for the client, but when the FIFO is not empty, e.g. writing in a new shell
$ echo "Hello" > MYFIFO
then the server enters an infinite loop, repeating the "Hello"-string
Received string: "Hello". Length is 4
Received string: "Hello". Length is 4
...
Furthermore, new strings written to the fifo doesn't seem to be read by the server. However, in Linux the behaviour is quite different. In Linux, the server prints the string and waits for new data to appear on the fifo. What is the reason for this discrepancy ?
You need to fix your code to remove at least 3 bugs:
You're not doing a close(fd) so you will get a file descriptor leak and eventually be unable to open() new files.
You're not checking the value of fd (if it returns -1 then there was an error).
You're not checking the value of read (if it returns -1 then there was an error)... and your readbuf[read_bytes] = '\0'; will not be doing what you expect as a result.
When you get an error then errno will tell you what went wrong.
These bugs probably explain why you keep getting Hello output (especially the readbuf[read_bytes] problem).

Linux Kernel: invoke call back function in user space from kernel space

I am writing Linux user space application. where I want to invoke registered callback function in user space area from the kernel space.
i.e. interrupt arriving on GPIO pin(switch press event) and registered function getting called in user space.
is there any method is available to do this.
Thanks
I found below code after lot of digging and perfectly works for me.
Handling interrupts from GPIO
In many cases, a GPIO input can be configured to generate an interrupt when it
changes state, which allows you to wait for the interrupt rather than polling in
an inefficient software loop. If the GPIO bit can generate interrupts, the file edge
exists. Initially, it has the value none , meaning that it does not generate interrupts.
To enable interrupts, you can set it to one of these values:
• rising: Interrupt on rising edge
• falling: Interrupt on falling edge
• both: Interrupt on both rising and falling edges
• none: No interrupts (default)
You can wait for an interrupt using the poll() function with POLLPRI as the event. If
you want to wait for a rising edge on GPIO 48, you first enable interrupts:
#echo 48 > /sys/class/gpio/export
#echo rising > /sys/class/gpio/gpio48/edge
Then, you use poll() to wait for the change, as shown in this code example:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>>
int main(void) {
int f;
struct pollfd poll_fds [1];
int ret;
char value[4];
int n;
f = open("/sys/class/gpio/gpio48", O_RDONLY);
if (f == -1) {
perror("Can't open gpio48");
return 1;
}
poll_fds[0].fd = f;
poll_fds[0].events = POLLPRI | POLLERR;
while (1) {
printf("Waiting\n");
ret = poll(poll_fds, 1, -1);
if (ret > 0) {
n = read(f, &value, sizeof(value));
printf("Button pressed: read %d bytes, value=%c\n", n, value[0]);
}
}
return 0;
}
Have to implement a handler in a kernel module that triggers e.g. a char device. From user space it could be accessed by polling (e.g. ioctl() calls). It seems that it is the only way at the moment.

Linux input device events, how to retrieve initial state

I am using the gpio-keys device driver to handle some buttons in an embedded device running Linux. Applications in user space can just open /dev/input/eventX and read input events in a loop.
My question is how to get the initial states of the buttons. There is an ioctl call (EVIOCGKEY) which can be used for this, however if I first check this and then start to read from /dev/input/eventX, there's no way to guarantee that the state did not change in between.
Any suggestions?
The evdev devices queue events until you read() them, so in most cases opening the device, doing the ioctl() and immediately starting to read events from it should work. If the driver dropped some events from the queue, it sends you a SYN_DROPPED event, so you can detect situations where that happened. The libevdev documentation has some ideas on how one should handle that situation; the way I read it you should simply retry, i.e. drop all pending events, and redo the ioctl() until there are no more SYN_DROPPED events.
I used this code to verify that this approach works:
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <string.h>
#define EVDEV "/dev/input/event9"
int main(int argc, char **argv) {
unsigned char key_states[KEY_MAX/8 + 1];
struct input_event evt;
int fd;
memset(key_states, 0, sizeof(key_states));
fd = open(EVDEV, O_RDWR);
ioctl(fd, EVIOCGKEY(sizeof(key_states)), key_states);
// Create some inconsistency
printf("Type (lots) now to make evdev drop events from the queue\n");
sleep(5);
printf("\n");
while(read(fd, &evt, sizeof(struct input_event)) > 0) {
if(evt.type == EV_SYN && evt.code == SYN_DROPPED) {
printf("Received SYN_DROPPED. Restart.\n");
fsync(fd);
ioctl(fd, EVIOCGKEY(sizeof(key_states)), key_states);
}
else if(evt.type == EV_KEY) {
// Ignore repetitions
if(evt.value > 1) continue;
key_states[evt.code / 8] ^= 1 << (evt.code % 8);
if((key_states[evt.code / 8] >> (evt.code % 8)) & 1 != evt.value) {
printf("Inconsistency detected: Keycode %d is reported as %d, but %d is stored\n", evt.code, evt.value,
(key_states[evt.code / 8] >> (evt.code % 8)) & 1);
}
}
}
}
After starting, the program deliberately waits 5 seconds. Hit some keys in that time to fill the buffer. On my system, I need to enter about 70 characters to trigger a SYN_DROPPED. The EV_KEY handling code checks if the events are consistent with the state reported by the EVIOCGKEY ioctl.

UART takes some "kicking" to start receiving data

I am using a UART on a single board computer (Olimex A13) and I am trying to send and receive data through the UART. On the send side I have no problems. The Olimex board sends data to the serial buss and it is received on an external PC that is running putty terminal.
When I try to go in the other direction (PC sends data to be received by the Olimex board) I am running into a weird problem. When I start to send data, initially no data shows up on the Olimex board (I have tried 2 methods... cat /dev/ttyS0 and setting up a receive C program. Both exhibit the same results).
If I send data for a while (seems to vary from less than 10 characters to something over 20) at some point the uart seems to "wake up" and now data appears on the Olimex board. Once the data transfer starts it will keep going as long as I want and at fast or slow data rates. If I break out of the receive program or end the cat /dev/ttyS0 process and try to start receiving again, I need to go through the same process of "kicking" the uart until it decides it wants to receive again.
I have put an oscilloscope on the RX pin leading into the UART. The data coming in is at good levels and the data appears here from the very first send so the data is not being blocked initially and then arriving at the RX pin at some time later. So I am currently assuming that the issue is with a UART setting of some kind. Does anyone know of a UART setting that could possibly explain this behavior?
Below is the receive program that I am running. I don't think that there is an issue with it because I see the same behavior when I do a cat /dev/ttyS0. Any help here would be appreciated.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#define BAUDRATE B115200
#define MODEMDEVICE "/dev/ttyS0"
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1
volatile int STOP=FALSE;
main()
{
int fd,c, res;
struct termios oldtio,newtio;
char buf[255];
fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY );
if (fd <0) {perror(MODEMDEVICE); return(-1); }
tcgetattr(fd,&oldtio); /* save current port settings */
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
/* set input mode (non-canonical, no echo,...) */
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
newtio.c_cc[VMIN] = 1; /* blocking read until 5 chars received */
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
while (STOP==FALSE) { /* loop for input */
res = read(fd,buf,255); /* returns after 5 chars have been input */
buf[res]=0; /* so we can printf... */
printf("String:%s - Character Count:%d\n", buf, res);
if (buf[0]=='z') STOP=TRUE;
}
tcsetattr(fd,TCSANOW,&oldtio);
}
Sawdust made these comments....
"sends data to the serial buss" -- RS232 is not a "buss". "When I
start to send data" -- Your writing style is confusing. What kind of
data is sent to the Olimex? For the cat /dev/ttyS0 test, how did you
set up the serial port, or was it in an unknown mode? bzero(&newtio,
sizeof(newtio)); -- That is a dangerous way to setup the termios
structure. newtio.c_cflag = BAUDRATE... That is not the proper way to
set the baud rate; you need to use the cfset[io]speed() functions. You
write of a "kick", yet you don't mention doing anything explicit
ok, RS232 is not a buss, I misspoke....
The data I am sending is a stream of characters that are typed on Putty terminal.
For the /dev/ttyS0 test the settings are the same as are set in the c program. I verified this using stty.
I copied this code from another post. I was not sure what bzero did. If you could expand on "dangerous" that would be great. Or better if you have a better way to do what bzero does?
I will update the way to set baud rate.. but send is working perfectly and I verified using stty that baudrate was indeed 115200.
As far as "kicking" I mean that I need to send a number of bytes of data to the uart before any appear on the console. Others have mentioned a FIFO that may be an issue here but the first characters that are sent are lost. For example if I were to send the string of characters abcdefg.... I could type up until k and then l would appear on the console, and then any future characters after l would also appear in the console. If I were to go through the same process again after starting the receive program (or cat /dev/ttyS0) the point where the receive starts appearing could be in a different place (i.e. at h or n).

Resources