I want to check the availability of an SD card and receive notifications for adding / removing an SD card.
So far I have used libudev
and I have made a small application that listens to SD card events.
The code is listed below:
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stddef.h> #include <errno.h> #include <sys/time.h> //debug -> remove me #include <libudev.h> #define ADD_FILTER "add" #define REMOVE_FILTER "remove" #define SUBSYSTEM_FILTER "block" #define ATTR_FILTER "ID_MODEL" #define SD_ATTR_VALUE "SD_MMC" #define ATTR_ACTIVE_SD "ID_PART_TABLE_TYPE" static bool isDeviceSD(struct udev_device *device); static bool isDevPresent(struct udev *device); static void print_device(struct udev_device *device, const char *source); //for debugging -> remove me static bool s_bSD_present; int main() { struct udev *udev; struct udev_monitor *udev_monitor = NULL; fd_set readfds; s_bSD_present = false; udev = udev_new(); if (udev == NULL) { printf("udev_new FAILED \n"); return 1; } s_bSD_present = isDevPresent(udev); if(s_bSD_present) { printf("+++SD is plugged in \n"); } else { printf("---SD is not plugged in \n"); } udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); if (udev_monitor == NULL) { printf("udev_monitor_new_from_netlink FAILED \n"); return 1; } //add some filters if( udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, SUBSYSTEM_FILTER, NULL) < 0 ) { printf("udev_monitor_filter_add_match_subsystem_devtype FAILED \n"); return 1; } if (udev_monitor_enable_receiving(udev_monitor) < 0) { printf("udev_monitor_enable_receiving FAILED \n"); return 1; } while (1) { printf("Polling for new data... \n"); int fdcount = 0; FD_ZERO(&readfds); if (udev_monitor != NULL) { FD_SET(udev_monitor_get_fd(udev_monitor), &readfds); } fdcount = select(udev_monitor_get_fd(udev_monitor)+1, &readfds, NULL, NULL, NULL); if (fdcount < 0) { if (errno != EINTR) printf("Error receiving uevent message\n"); continue; } if ((udev_monitor != NULL) && FD_ISSET(udev_monitor_get_fd(udev_monitor), &readfds)) { struct udev_device *device; device = udev_monitor_receive_device(udev_monitor); if (device == NULL) continue; //check the action const char* szAction = udev_device_get_action(device); if( strcmp(szAction, ADD_FILTER) == 0) { if( !s_bSD_present && isDeviceSD(device) ) { s_bSD_present = true; printf("+++SD has been plugged in \n"); } } else if( strcmp(szAction, REMOVE_FILTER) == 0 ) { if( s_bSD_present && isDeviceSD(device) ) { s_bSD_present = false; printf("---SD has been removed \n"); } } udev_device_unref(device); } } return 0; } static bool isDeviceSD(struct udev_device *device) { bool retVal = false; struct udev_list_entry *list_entry = 0; struct udev_list_entry* model_entry = 0; struct udev_list_entry* active_sd_entry = 0; list_entry = udev_device_get_properties_list_entry(device); model_entry = udev_list_entry_get_by_name(list_entry, ATTR_FILTER); if( 0 != model_entry ) { const char* szModelValue = udev_list_entry_get_value(model_entry); active_sd_entry = udev_list_entry_get_by_name(list_entry, ATTR_ACTIVE_SD); if(strcmp(szModelValue, SD_ATTR_VALUE) == 0 && active_sd_entry != 0) { printf("Device is SD \n"); retVal = true; //print_device(device, "UDEV"); } } return retVal; } static bool isDevPresent(struct udev *device) { bool retVal = false; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *dev_list_entry; enumerate = udev_enumerate_new(device); udev_enumerate_add_match_subsystem(enumerate, SUBSYSTEM_FILTER); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(dev_list_entry, devices) { struct udev_device *dev; const char* dev_path = udev_list_entry_get_name(dev_list_entry); dev = udev_device_new_from_syspath(device, dev_path); if( true == isDeviceSD(dev) ) { retVal = true; udev_device_unref(dev); break; } udev_device_unref(dev); } udev_enumerate_unref(enumerate); return retVal; } static void print_device(struct udev_device *device, const char *source) { struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); printf("%-6s[%llu.%06u] %-8s %s (%s)\n", source, (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec, udev_device_get_action(device), udev_device_get_devpath(device), udev_device_get_subsystem(device)); struct udev_list_entry *list_entry; udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) printf("%s=%s\n", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); printf("\n"); }
This code will receive notifications about adding / removing the SD card (and the initial state of the SD-connected / disconnected). However, this is more of a hack and it does not work in all cases.
Currently, I am using the device ID_MODEL
attribute and checking if it has SD_MMC
for SD cards. At the moment, I only need this type of card, so enough.
When an SD card is inserted, the following events are sent for the subsystem block: 2 change
events and 1 add
event for each section. Event properties are listed below:
<----- change event - subsystem block - disk type disk -----> UDEV [1339412734.522055] change /devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:2/block/sdd (block) UDEV_LOG=3 ACTION=change DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:2/block/sdd SUBSYSTEM=block DEVNAME=/dev/sdd DEVTYPE=disk SEQNUM=3168 ID_VENDOR=Generic- ID_VENDOR_ENC=Generic- ID_VENDOR_ID=0bda ID_MODEL=SD_MMC ID_MODEL_ENC=SD\x2fMMC\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20 ID_MODEL_ID=0151 ID_REVISION=1.00 ID_SERIAL=Generic-_SD_MMC_20060413092100000-0:2 ID_SERIAL_SHORT=20060413092100000 ID_TYPE=disk ID_INSTANCE=0:2 ID_BUS=usb ID_USB_INTERFACES=:080650: ID_USB_INTERFACE_NUM=00 ID_USB_DRIVER=usb-storage ID_PATH=pci-0000:02:03.0-usb-0:1:1.0-scsi-0:0:0:2 ID_PART_TABLE_TYPE=dos UDISKS_PRESENTATION_NOPOLICY=0 UDISKS_PARTITION_TABLE=1 UDISKS_PARTITION_TABLE_SCHEME=mbr UDISKS_PARTITION_TABLE_COUNT=2 MAJOR=8 MINOR=48 DEVLINKS=/dev/block/8:48 /dev/disk/by-id/usb-Generic-_SD_MMC_20060413092100000-0:2 /dev/disk/by-path/pci-0000:02:03.0-usb-0:1:1.0-scsi-0:0:0:2 <----- add event partition 1 - subsystem block - disk type partition -----> UDEV [1339412734.719107] add /devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:2/block/sdd/sdd1 (block) UDEV_LOG=3 ACTION=add DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:2/block/sdd/sdd1 SUBSYSTEM=block DEVNAME=/dev/sdd1 DEVTYPE=partition SEQNUM=3169 ID_VENDOR=Generic- ID_VENDOR_ENC=Generic- ID_VENDOR_ID=0bda ID_MODEL=SD_MMC ID_MODEL_ENC=SD\x2fMMC\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20 ID_MODEL_ID=0151 ID_REVISION=1.00 ID_SERIAL=Generic-_SD_MMC_20060413092100000-0:2 ID_SERIAL_SHORT=20060413092100000 ID_TYPE=disk ID_INSTANCE=0:2 ID_BUS=usb ID_USB_INTERFACES=:080650: ID_USB_INTERFACE_NUM=00 ID_USB_DRIVER=usb-storage ID_PATH=pci-0000:02:03.0-usb-0:1:1.0-scsi-0:0:0:2 ID_PART_TABLE_TYPE=dos ID_FS_UUID=6343c7b9-92a9-4d8f-bdd8-893f1190f294 ID_FS_UUID_ENC=6343c7b9-92a9-4d8f-bdd8-893f1190f294 ID_FS_VERSION=1.0 ID_FS_TYPE=ext2 ID_FS_USAGE=filesystem UDISKS_PRESENTATION_NOPOLICY=0 UDISKS_PARTITION=1 UDISKS_PARTITION_SCHEME=mbr UDISKS_PARTITION_NUMBER=1 UDISKS_PARTITION_TYPE=0x83 UDISKS_PARTITION_SIZE=1006919680 UDISKS_PARTITION_SLAVE=/sys/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:2/block/sdd UDISKS_PARTITION_OFFSET=11618304 UDISKS_PARTITION_ALIGNMENT_OFFSET=0 MAJOR=8 MINOR=49 DEVLINKS=/dev/block/8:49 /dev/disk/by-id/usb-Generic-_SD_MMC_20060413092100000-0:2-part1 /dev/disk/by-path/pci-0000:02:03.0-usb-0:1:1.0-scsi-0:0:0:2-part1 /dev/disk/by-uuid/6343c7b9-92a9-4d8f-bdd8-893f1190f294 <----- add event partition 2 - subsystem block - disk type partition -----> UDEV [1339412734.731338] add /devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:2/block/sdd/sdd2 (block) UDEV_LOG=3 ACTION=add DEVPATH=/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:2/block/sdd/sdd2 SUBSYSTEM=block DEVNAME=/dev/sdd2 DEVTYPE=partition SEQNUM=3170 ID_VENDOR=Generic- ID_VENDOR_ENC=Generic- ID_VENDOR_ID=0bda ID_MODEL=SD_MMC ID_MODEL_ENC=SD\x2fMMC\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20 ID_MODEL_ID=0151 ID_REVISION=1.00 ID_SERIAL=Generic-_SD_MMC_20060413092100000-0:2 ID_SERIAL_SHORT=20060413092100000 ID_TYPE=disk ID_INSTANCE=0:2 ID_BUS=usb ID_USB_INTERFACES=:080650: ID_USB_INTERFACE_NUM=00 ID_USB_DRIVER=usb-storage ID_PATH=pci-0000:02:03.0-usb-0:1:1.0-scsi-0:0:0:2 ID_PART_TABLE_TYPE=dos UDISKS_PRESENTATION_NOPOLICY=0 UDISKS_PARTITION=1 UDISKS_PARTITION_SCHEME=mbr UDISKS_PARTITION_NUMBER=2 UDISKS_PARTITION_TYPE=0xda UDISKS_PARTITION_SIZE=11618304 UDISKS_PARTITION_FLAGS=boot UDISKS_PARTITION_SLAVE=/sys/devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host11/target11:0:0/11:0:0:2/block/sdd UDISKS_PARTITION_OFFSET=1022410752 UDISKS_PARTITION_ALIGNMENT_OFFSET=0 MAJOR=8 MINOR=50 DEVLINKS=/dev/block/8:50 /dev/disk/by-id/usb-Generic-_SD_MMC_20060413092100000-0:2-part2 /dev/disk/by-path/pci-0000:02:03.0-usb-0:1:1.0-scsi-0:0:0:2-part2
From the change
event, I cannot extract information about whether the device was added or removed. I tried to open the device name \dev\sdd
, but I do not have permissions, so the option crashes ...
Now I just check the action attribute for the sections ( add
/ remove
).
This version of the program works very well for SD cards with partitions. When there are no partitions, only change
events are accepted.
So my question is : is there a way to check if the media has been added / removed from the change
event? Or is there another way to check if the device is accessible (given the partition problem)?
Any suggestion to improve the iteration of a device attribute or method of receiving notifications would be welcome.
PS And I can not use libusb
:).