我需要检查SD卡的存在,并在SD卡添加/移除时接收通知。
到目前为止,我使用了libudev
,并制作了一个监听SD卡事件的小应用程序。
以下是代码:
#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卡。现在我只需要这种类型的卡片,所以这就足够了。
当插入SD卡时,将发送以下事件到子系统块:2个“change”事件和每个分区的1个“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
,但我没有权限,所以这个选项不可用...目前,我只是检查分区的操作属性(add
/remove
)。对于有分区的SD卡,此程序版本效果很好。当没有分区时,只接收到change
事件。因此,我的问题是:是否有任何方法可以从change
事件中检查媒体是添加还是移除?或者是否有其他方法可以检查设备是否可用(考虑到分区问题)?欢迎提出有关改进设备属性迭代或获取通知方法的任何建议。P.S.我不能使用libusb
:)。
DEVNAME
值(\dev\sdd
)上调用 open 函数,但是我没有权限,所以调用总是失败。还有其他方法来检查设备是否存在吗? - Alexandru C.\dev\sdd
上运行stat
命令,无论SD卡是否插入,输出都相同,因此我不能使用它... - Alexandru C.