我想检查SD卡是否存在,并接收有关SD卡添加/删除的通知。
到目前为止,我已经使用过libudev,并且已经制作了一个小应用程序来监听SD卡事件。
libudev
下面列出了代码:
#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"); }
此代码将获取有关添加/删除SD卡的通知(以及SD的初始状态-已插入/未插入)。但是,它更像是一种黑客手段,并非在所有情况下都有效。
我目前使用ID_MODEL设备的属性,并检查它是否SD_MMC为-SD卡。我现在只需要这种类型的卡,就足够了。
ID_MODEL
SD_MMC
插入SD卡后,将为子系统块发送以下事件:2个change事件,add每个分区1个事件。下面列出了事件属性:
change
add
<----- 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
从change事件中,我无法提取有关是否添加或删除设备的任何信息。我试图打开设备名称,\dev\sdd但是我没有权限,所以该选项属于…
\dev\sdd
现在,我只是检查分区(add/ remove)的action属性。
remove
该程序的该版本非常适合具有分区的SD卡。如果没有分区,则仅change接收事件。
所以我的问题是 :有什么方法可以检查是否已从change事件中添加/删除媒体?还是有其他方法检查设备是否可用(请记住分区问题)?
任何有关改进设备属性迭代或获取通知方法的建议都将受到欢迎。
PS,我不能使用libusb:)。
libusb
好。因此,我可以在没有分区的SD卡PC上使用它。
更新的代码是这样的:
#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 <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <libudev.h> #define ADD_FILTER "add" #define REMOVE_FILTER "remove" #define SUBSYSTEM_FILTER "block" #define DEVTYPE_FILTER "disk" #define ATTR_FILTER "ID_MODEL" #define SD_ATTR_VALUE "SD_MMC" #define ATTR_ADDED_DISK "UDISKS_PARTITION_TABLE" // attribute is available for "change" event when SD card is added (n/a when removed) static bool isDeviceSD(struct udev_device *device); //checks if device is SD card (MMC) static bool isDevPresent(struct udev *device); //checks if device is present (SD + added) static bool isDeviceAdded(struct udev_device *device); //checks if device is added (presence of attribute ATTR_ADDED_DISK) 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; } if( isDevPresent(udev) ) { s_bSD_present = true; 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, DEVTYPE_FILTER) < 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 presence if( isDeviceSD(device) && isDeviceAdded(device) ) { if(!s_bSD_present) //guard for double "change" events { s_bSD_present = true; printf("+++SD has been plugged in \n"); } } else { if(s_bSD_present) //not needed -> just keeping consistency { 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; 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); if( strcmp( szModelValue, SD_ATTR_VALUE) == 0 ) { //printf("Device is SD \n"); retVal = true; //print_device(device, "UDEV"); } } return retVal; } static bool isDeviceAdded(struct udev_device *device) { bool retVal = false; struct udev_list_entry *list_entry = 0; struct udev_list_entry* added_disk_entry = 0; list_entry = udev_device_get_properties_list_entry(device); added_disk_entry = udev_list_entry_get_by_name(list_entry,/* "DEVNAME" */ ATTR_ADDED_DISK); if( 0 != added_disk_entry ) { retVal = true; } 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( isDeviceSD(dev) && isDeviceAdded(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"); }
解决方案(仍然不是很明亮)正在检查某些属性,该属性仅在添加SD卡时才可用(例如UDISKS_PARTITION_TABLE)。
UDISKS_PARTITION_TABLE
这在x86上效果很好。