I am creating a test application for a new platform that we are working on to test libudev
and make sure our C
application will be able to be properly notified when a particular usb
device is inserted or removed.
On our system every time this device is removed and added it's mounted in a different location such as /dev/hidraw0
or /dev/hidraw1
etc. So my test application uses libudev
to both monitor events and enumerate through the hidraw
devices so it can find where it's been mounted. Every time the device status changes, insertion or removal, the code finds where it's mounted and restarts libudev
event monitoring.
My questions:
1- I see the "add" events. But why do I never see the remove events?
2- Am I properly closing and restarting the libudev
monitoring such that resources aren't being leaked?
Sample output:
Found UGA. SysPath=[/sys/class/hidraw/hidraw2] DevPath=[/dev/hidraw2]
Starting monitor returned 2UGA added....
Found UGA. SysPath=[/sys/class/hidraw/hidraw2] DevPath=[/dev/hidraw2]
Starting monitor returned 2UGA added....
Found UGA. SysPath=[/sys/class/hidraw/hidraw1] Dev Path=[/dev/hidraw1]
Starting monitor returned 2
EDIT:
What I've seen is that this test application is receiving the remove
events when run on our much older x86 platform with a 2.6 kernel while the same code running on our new ARM platform with a 3.10.66 kernel doesn't receive the remove
event.
Could this be a udev
/libudev
system configuration or kernel issue?
Also when running udevadm monitor
on the ARM board the required remove
events are being sent as shown below. So why isn't libudev
not forwarding them to my application?
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)
Test Code:
/* 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 );
}