2012-06-11 62 views
6

我想检查是否存在SD卡,并接收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)action属性。

该版本的程序对于具有分区的SD卡非常适用。当没有分区时,只收到change事件。

所以我的问题是:有什么办法可以检查媒体是否被添加/从change事件中删除?或者是否有其他方法来检查设备是否可用(请记住分区问题)?

任何有关改进设备属性迭代或获取通知的方法的建议都会受到欢迎。

P.S.而且我不能使用libusb :)。

+0

难道你不能检查事件发生后块设备是否存在? – Behrooz

+0

呃,我试着在'DEVNAME'值('\ dev \ sdd')上打开[打开](http://linux.die.net/man/2/open),但我没有权限,所以通话总是失败。是否有其他方法来检查设备是否存在? –

+0

http://www.linuxquestions.org/questions/programming-9/checking-if-a-file-exists-in-c-21700/ – Behrooz

回答

1

好的。所以我在没有分区的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)。

这适用于x86。