I am trying to debug an issue with random key presses being registered even though I have not typed them. I had started the question here.
Initially I ran evtest and specified each device. I could clearly see the random characters appear but evtest was not registering the keypress. I spent a lot of time rerunning evtest each time only monitoring for 1 input device. In an act of impatience, I modified evtest.c by replacing the scan_devices function with the hope of whenever a keypress event happens it will monitor all devices and notify me which device triggered the keypress. Below code is based on this:
static char* scan_devices(void)
{
struct dirent **namelist;
int i, ndev, devnum;
char *filename;
int max_device = 0;
ndev = scandir(DEV_INPUT_EVENT, &namelist, is_event_device, versionsort);
if (ndev <= 0)
return NULL;
fprintf(stderr, "Available devices:\n");
for (i = 0; i < ndev; i++){
char fname[300];
int fd = -1;
char name[256] = "???";
snprintf(fname, sizeof(fname),
"%s/%s", DEV_INPUT_EVENT, namelist[i]->d_name);
fd = open(fname, O_RDONLY);
if (fd < 0)
continue;
ioctl(fd, EVIOCGNAME(sizeof(name)), name);
fprintf(stderr, "%s: %s\n", fname, name);
close(fd);
sscanf(namelist[i]->d_name, "event%d", &devnum);
if (devnum > max_device)
max_device = devnum;
free(namelist[i]);
}//end for
/* open and monitor nuevents files*/
struct input_event ev[64];
int rd;
fd_set rdfs;
int nuevents=28;
int fd[28]; //should be equal to nuevents
int ret=0;
printf("Size of rdfs %d B \nStart Monitor \n", (int) sizeof(rdfs));
/* Next we clear the file descriptors*/
FD_ZERO(&rdfs);
/* We need to open each device and then add them to the file descriptors */
for (i = 0; i < nuevents; i++){
char fname[300];
snprintf(fname, sizeof(fname),
"%s/%s", DEV_INPUT_EVENT, namelist[i]->d_name);
fd[i] = open(fname, O_RDONLY);
if (fd[i]<0){
printf("error file %d \n",(int)i);
return 0;
}// end if
FD_SET(fd[i], &rdfs);
}// end for
while (!stop) {
/*
https://www.man7.org/linux/man-pages/man2/select.2.html
*/
ret = select(nuevents, &rdfs, NULL, NULL, NULL);
if (stop)
break;
if (ret < 0){
printf("ret failure");
exit (EXIT_FAILURE);
}// end if
for (i=0;i<nuevents;i++){
if (FD_ISSET(i,&rdfs)){
printf("received data on device %d\n",(int)i);
rd = read(fd[i], ev, sizeof(ev));
if (rd < (int) sizeof(struct input_event)) {
printf("expected %d bytes, got %d\n", (int) sizeof(struct input_event), rd);
perror("\nevtest: error reading");
return "error";
} // end of if
}// end of if
}// end of for[
}// end of while
for (i = 0; i < nuevents; i++){
ioctl(fd[i], EVIOCGRAB, (void*)0); /* Use IO CTRL to release device*/
}//end for
return "done";
}
The output looks generally correct but if I manually type an event the character shows up but an event never triggers so I must be doing something wrong. I did run it as root. Example output is below.
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0: Power Button
/dev/input/event1: Sleep Button
/dev/input/event2: Lid Switch
/dev/input/event3: Power Button
/dev/input/event4: AT Translated Set 2 keyboard
/dev/input/event5: System76 ACPI Hotkeys
/dev/input/event6: FTCS1000:00 2808:0102 Mouse
/dev/input/event7: FTCS1000:00 2808:0102 Touchpad
/dev/input/event8: MOSART Semi. 2.4G INPUT DEVICE
/dev/input/event9: MOSART Semi. 2.4G INPUT DEVICE Mouse
/dev/input/event10: MOSART Semi. 2.4G INPUT DEVICE Consumer Control
/dev/input/event11: MOSART Semi. 2.4G INPUT DEVICE System Control
/dev/input/event12: MOSART Semi. 2.4G INPUT DEVICE
/dev/input/event13: Lite-On Technology Corp. USB Multimedia Keyboard
/dev/input/event14: Lite-On Technology Corp. USB Multimedia Keyboard System Control
/dev/input/event15: Lite-On Technology Corp. USB Multimedia Keyboard Consumer Control
/dev/input/event16: Lite-On Technology Corp. USB Multimedia Keyboard
/dev/input/event17: Video Bus
/dev/input/event18: Intel HID events
/dev/input/event19: HDA Intel PCH Mic
/dev/input/event20: HDA Intel PCH Headphone
/dev/input/event21: HDA Intel PCH HDMI/DP,pcm=3
/dev/input/event22: HDA Intel PCH HDMI/DP,pcm=7
/dev/input/event23: HDA Intel PCH HDMI/DP,pcm=8
/dev/input/event24: HDA Intel PCH HDMI/DP,pcm=9
/dev/input/event25: HDA NVidia HDMI/DP,pcm=3
/dev/input/event26: HDA NVidia HDMI/DP,pcm=7
/dev/input/event27: HDA NVidia HDMI/DP,pcm=8
/dev/input/event28: HDA NVidia HDMI/DP,pcm=9
Size of rdfs 128 B
Start Monitor
received data on device 7
a
a
At the end I pressed the "A" button on the keyboard but did not get a message of which device was pressed that I expected. Any help of ideas to try to fix this?
After adding a large amount of printf I found that the program seems to be hanging during the statement:
rd = read(fd[i], ev, sizeof(ev));
This line in the original code looks suspicious:
if (FD_ISSET(i,&rdfs)){
According to man select FD_ISSET()
does the following:
select()
modifies the contents of the sets according to the rules described below. After callingselect()
, theFD_ISSET()
macro can be used to test if a file descriptor is still present in a set.FD_ISSET()
returns nonzero if the file descriptorfd
is present in set, and zero if it is not.
So, in the original code
for (i=0;i<nuevents;i++){
if (FD_ISSET(i,&rdfs)){
FD_ISSET
checks whether a value held in the variable i
is present in the set rdfs
, but the set consists of file descriptors held in the array int fd[28]
:
FD_SET(fd[i], &rdfs);
Therefore, the check should also be done against this array:
if (FD_ISSET(fd[i],&rdfs)){
After adding a large amount of printf I found that the program seems to be hanging during the statement:
rd = read(fd[i], ev, sizeof(ev));
Your discovery makes sense. During one of the iterations of the for
loop the erroneous code if (FD_ISSET(i,&rdfs)){
returns true
for some integer in range 0 - 27 (whatever the current value of i
is). The code proceeds to reading data by calling read
, and passes it a file descriptor fd[i]
, which is not guaranteed to be the same as i
and thus not guaranteed to have any data available for reading. If there is no data to be read from the file descriptor fd[i]
, the read
call blocks the execution of the calling thread.
That being said, replace if (FD_ISSET(i,&rdfs)){
with if (FD_ISSET(fd[i],&rdfs)){