c - Linux libudev 测试应用程序未接收到 REMOVE 事件

标签 c linux usb udev

我正在为我们正在努力测试 libudev 的新平台创建一个测试应用程序,并确保我们的 C 应用程序能够在插入或移除特定的 usb 设备。

在我们的系统上,每次移除和添加此设备时,它都会安装在不同的位置,例如 /dev/hidraw0/dev/hidraw1 等。所以我的测试应用程序使用 libudev 来监视事件并通过 hidraw 设备进行枚举,以便它可以找到它的安装位置。每次设备状态发生变化,插入或移除时,代码都会找到它的挂载位置并重新启动 libudev 事件监控。

我的问题:

1- 我看到“添加”事件。但是为什么我从来没有看到删除事件?
2- 我是否正确关闭并重新启动了 libudev 监控,以防止资源泄漏?

示例输出:

Found UGA. SysPath=[/sys/class/hidraw/hidraw2] DevPath=[/dev/hidraw2]
Starting monitor returned 2

UGA added....
Found UGA. SysPath=[/sys/class/hidraw/hidraw2] DevPath=[/dev/hidraw2]
Starting monitor returned 2

UGA added....
Found UGA. SysPath=[/sys/class/hidraw/hidraw1] Dev Path=[/dev/hidraw1]
Starting monitor returned 2

编辑:
我所看到的是,此测试应用程序在我们具有 2.6 内核的更旧的 x86 平台上运行时正在接收 remove 事件,而相同的代码在我们具有 3.10.66 内核的新 ARM 平台上运行没有收到 remove 事件。

这可能是 udev/libudev 系统配置或内核问题?

此外,当在 ARM 板上运行 udevadm monitor 时,所需的 remove 事件正在发送,如下所示。那么为什么 libudev 不将它们转发到我的应用程序呢?

box:/home/eng$ udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[80296.266707] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001D (hid)
KERNEL[80296.266864] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0 (usb)
UDEV  [80296.267553] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001D (hid)
KERNEL[80296.267695] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1 (usb)
UDEV  [80296.268031] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0 (usb)
UDEV  [80296.268890] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1 (usb)
KERNEL[80304.423516] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1 (usb)
KERNEL[80304.424514] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0 (usb)
KERNEL[80304.427211] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001E (hid)
KERNEL[80304.458031] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001E/hidraw/hidraw1 (hidraw)
UDEV  [80304.460096] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1 (usb)
UDEV  [80304.467105] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0 (usb)
UDEV  [80304.472885] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001E (hid)
UDEV  [80304.474903] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001E/hidraw/hidraw1 (hidraw)

测试代码:

/* Linux */
#include <linux/input.h>
#include <linux/hidraw.h>
#include <linux/fd.h>

/* Unix */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <libudev.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>

/* C */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>
#include <stdbool.h>
#include <stdint.h>

#ifndef FALSE
#define FALSE 0
#define TRUE 1
#endif


struct udev *udev;
struct udev_monitor *udevMonitor;
int mon_fd = -1;
int fd = -1;
char sysPath[50];
char devPath[50];


enum { DEVICE_NO_CHANGE = 0, DEVICE_ADD, DEVICE_REMOVE, DEVICE_MOVED };
enum { OFFLINE = 0, ONLINE };
enum { ENABLED = 1, DISABLED };
enum { FAILED = 0, PENDING , ACTIVE };


void msSleep(int milliseconds);
void GetUGAMountPoints(void);
int StartUDEVMonitor(void);
int usb_check_device_status(void);

void GetUGAMountPoints(void)
{
    struct udev_device *dev;
    struct udev_enumerate *enumerate;
    struct udev_list_entry *devices, *dev_list_entry;

    strcpy( devPath, "/dev/hidraw" );

    /* Create a list of the devices in the 'hidraw' subsystem. */
    enumerate = udev_enumerate_new(udev);
    udev_enumerate_add_match_subsystem(enumerate, "hidraw");
    udev_enumerate_scan_devices(enumerate);
    devices = udev_enumerate_get_list_entry(enumerate);

    udev_list_entry_foreach(dev_list_entry, devices)
    {
        const char *path;

        path = udev_list_entry_get_name(dev_list_entry);
        dev = udev_device_new_from_syspath(udev, path);
        dev = udev_device_get_parent_with_subsystem_devtype( dev, "usb", "usb_device");
        if (!dev)
        {
            printf("Unable to find parent usb device.\n");
        }
        else
        {
            if( !strcmp(udev_device_get_sysattr_value(dev, "idVendor"), "0a70") &&
                !strcmp(udev_device_get_sysattr_value(dev, "idProduct"), "0441") )
            {
                char num = path[ strlen(path) - 1 ];
                devPath[ strlen(devPath) ] = num;

                /* Yes I know this sprintf() is insecure and lazy
                   This code is just for testing and not going into
                   production in this form
                */
                sprintf(sysPath, "/sys/class/hidraw/hidraw%c", num);

                printf("Found UGA. SysPath=[%s] Dev Path=[%s]\n", sysPath, devPath);
                udev_device_unref(dev);
                break;
            }
            else
            {
                udev_device_unref(dev);
            }
        }
    }

    udev_enumerate_unref(enumerate);
    return;
}


int StartUDEVMonitor(void)
{
    int retCode = ACTIVE;

    /* Create the udev object */
    udev = udev_new();
    if (!udev)
    {
        retCode = FAILED;
    }
    else
    {
        GetUGAMountPoints();

        /* Set up a monitor to monitor hidraw devices */
        udevMonitor = udev_monitor_new_from_netlink(udev, "kernel");
        udev_monitor_filter_add_match_subsystem_devtype(udevMonitor, "hidraw", NULL);
        udev_monitor_enable_receiving(udevMonitor);

        mon_fd = udev_monitor_get_fd(udevMonitor);
        fd = open(devPath, O_RDWR | O_NONBLOCK);

        if (fd < 0)
        {
            retCode = PENDING;
        }
    }
    return( retCode );
}

int usb_check_device_status(void)
{
    struct udev_device *dev;
    char action[25];  
    int retCode = DEVICE_NO_CHANGE;

    fd_set fds;
    struct timeval tv;
    int ret;

    FD_ZERO(&fds);
    FD_SET(mon_fd, &fds);
    tv.tv_sec = 0;
    tv.tv_usec = 75000;

    do
    {
        ret = select(mon_fd+1, &fds, NULL, NULL, &tv);

        /* Check if our file descriptor has received data. */
        if (ret > 0 && FD_ISSET(mon_fd, &fds))
        {
            FD_CLR(mon_fd, &fds);
            /* Make the call to receive the device.
               select() ensured that this will not block. */
            dev = udev_monitor_receive_device(udevMonitor);
            if (dev)
            {
                strcpy( action, udev_device_get_action(dev));
                printf("usb_check_device_status()::[%s]\n", action);

                if( !strcmp("remove",action))
                {
                    printf("UGA removed....\n");
                    retCode = DEVICE_REMOVE;
                }
                else if( !strcmp("add",action))
                {
                    printf("UGA added....\n");
                    retCode = DEVICE_ADD;
                }
                else if( !strcmp("move",action))
                {
                    printf("UGA moved....\n");
                    retCode = DEVICE_MOVED;
                }
                udev_device_unref(dev);
            }
        }
    }while( ret != 0 );
    return( retCode );
}


void msSleep(int milliseconds)
{
      usleep(milliseconds * 1000);
}


int main( int argc, char *argv[] )
{
    int status = StartUDEVMonitor();
    printf("Starting monitor returned %d\n",status);

    if(status == ACTIVE )
    {
        while(1)
        {
            if( usb_check_device_status() != DEVICE_NO_CHANGE )
            {
                close(fd);
                close(mon_fd);
                udev_monitor_unref(udevMonitor);
                udev_unref(udev);
                StartUDEVMonitor();
                printf("Starting monitor returned %d\n\n",status);
            }
            msSleep(75);
        }
    }
    return( 1 );
}

最佳答案

因此,通过查看 udevadm monitor 的输出,我能够看到在 ARM 板上的较新内核上,remove 事件没有被标记 使用 hidraw 就像使用 add 事件一样,如下所示:

box:/home/eng$ udevadm monitor
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent

KERNEL[80296.266707] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001D (hid)
KERNEL[80296.266864] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0 (usb)
UDEV  [80296.267553] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001D (hid)
KERNEL[80296.267695] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1 (usb)
UDEV  [80296.268031] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0 (usb)
UDEV  [80296.268890] remove   /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1 (usb)
KERNEL[80304.423516] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1 (usb)
KERNEL[80304.424514] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0 (usb)
KERNEL[80304.427211] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001E (hid)
KERNEL[80304.458031] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001E/hidraw/hidraw1 (hidraw)
UDEV  [80304.460096] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1 (usb)
UDEV  [80304.467105] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0 (usb)
UDEV  [80304.472885] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001E (hid)
UDEV  [80304.474903] add      /devices/soc0/soc.1/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb2/2-1/2-1.1/2-1.1:1.0/0003:0A70:0441.001E/hidraw/hidraw1 (hidraw)

因此,通过更改 StartUDEVMonitor() 中的过滤器设置:

udev_monitor_filter_add_match_subsystem_devtype(udevMonitor, "hidraw", NULL);

致:

udev_monitor_filter_add_match_subsystem_devtype(udevMonitor, "hid", NULL);

应用程序现在可以在具有较新内核的较新平台上可靠地接收 removeadd 事件。

关于c - Linux libudev 测试应用程序未接收到 REMOVE 事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28886103/

相关文章:

将二进制字符串转换为整数

linux - 使用输入/输出重定向在后台运行进程

C#检测usb设备ClassCode(usb设备类型)

检查指针是否指向给定数组

c - 不带分隔符分割字符数据

c - 从命令行读取可变行文件并存储

c - 如何在 mmaped 内存中删除脏页并为快速 munmap 做准备?

linux - Debian 上的 ImageMagick 安装

c - 为什么拔掉 USB 线后 select 返回?

c++ - 简单的 USB 主机堆栈