2

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));  
3
  • 1
    Unless you put the terminal in raw mode, input isn't made available to the application until you press Enter or the EOF character (default Control-d). You need to use raw mode to process input a character at a time. Commented Sep 6, 2024 at 16:33
  • Thank you for the feedback. I am not sure I understand completely. In the output shown I was hitting the letter and then enter. That is why the letters are on different lines. But also, I thought (maybe incorrectly) that probing the dev/input/event was the lower level access to the HW and the control of if information was passed to an App was one level above this. example Am I incorrect? Is this not exactly what the HW sees? I will try again with CTRL D. Commented Sep 7, 2024 at 2:10
  • @Barmar: the devices in /dev/input are NOT terminals, so don't have terminal modes or disciplines. They're input devices for X (windows) mulitplexing. Commented Sep 8, 2024 at 23:39

2 Answers 2

0

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 calling select(), the FD_ISSET() macro can be used to test if a file descriptor is still present in a set. FD_ISSET() returns nonzero if the file descriptor fd 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)){

Sign up to request clarification or add additional context in comments.

Comments

0

I was able to fix my issue, although I am not 100% sure why the change yet. I replaced:

rd = read(fd[i], ev, sizeof(ev));

With:

rd = read(i, ev, sizeof(ev));

I don't understand this because I assume that we would pass a reference to the file we opened and some of the earlier examples I had followed had actually used fd for the read, but I noticed in the multi event example they used i instead of fd[i]. 3

3 Comments

rd = read(i, ev, sizeof(ev)); is wrong. See my answer.
@VL-80 Thank you. After testing I confirmed your answer is correct. I had started off with fd[i] in the read. And changing to i had caused it to work but it was just because it aligned with FD_ISSET. It was a case of two wrongs causing it to work, just not work correctly. In reality the correct fix was to change the FD_ISSET to fd[i]. Thank you very much. I really appreciate the help to everyone that provided feedback!
Very good. Consider accepting the answer as correct by clicking a check mark next to it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.