简体   繁体   English

如何获取SD卡事件的通知?

[英]How to get notifications for SD card events?

I want to check the presence of an SD card, and receive notifications for SD card add/remove. 我想检查是否存在SD卡,并接收SD卡添加/删除的通知。

So far I have used libudev , and I've made a small application which listens for SD card events. 到目前为止,我已经使用了libudev ,并且我已经制作了一个小应用程序来监听SD卡事件。

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 get notifications for SD card add/remove (and the initial state of the SD - plugged/unplugged). 此代码将收到SD卡添加/删除的通知(以及SD的初始状态 - 插入/拔出)。 However, it is more of a hack, and it doesn't work in all cases. 但是,它更像是一种黑客攻击,并不适用于所有情况。

I currently use the ID_MODEL attribute of the device and check if it is SD_MMC - for SD cards. 我目前使用设备的ID_MODEL属性并检查它是否为SD_MMC - 用于SD卡。 I need only this type of card for now, so it's 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 partition. 插入SD卡时,会为子系统块发送以下事件:2个change事件,每个分区add 1个事件。 The 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 any information regarding whether the device was added or removed. change事件中,我无法提取有关是否添加或删除设备的任何信息。 I have tried to open the device name \\dev\\sdd but I don't have permissions, so that option falls... 我试图打开设备名称\\dev\\sdd但我没有权限,所以该选项会...

For now I'm just checking the action attribute for the partitions ( add / remove ). 现在我只是检查分区的action属性( add / remove )。

This version of the program works pretty well for SD cards that have partitions. 这个版本的程序适用于具有分区的SD卡。 When there are no partitions, only the change events are received. 如果没有分区,则仅接收change事件。

So my question is : is there any way I can check whether the media was added/removed from the change event? 所以我的问题是 :有什么方法可以检查媒体是否已从change事件中添加/删除? Or is there some other way to check if a device is available (keeping in mind the partition issue)? 或者是否有其他方法来检查设备是否可用(请记住分区问题)?

Any suggestion on improving the device attribute iteration or the method of getting the notifications would be welcomed. 任何有关改进设备属性迭代或获取通知的方法的建议都会受到欢迎。

PS And I can't use libusb :). PS我不能使用libusb :)。

Ok. 好。 So I got it working on the PC for SD cards without partitions. 所以我让它在没有分区的SD卡上工作。

The updated code is this: 更新的代码是这样的:

#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");

}

The solution (still not very bright) is checking for some attribute that is available only when SD card is added (like UDISKS_PARTITION_TABLE ). 解决方案(仍然不是很亮)是检查一些仅在添加SD卡时可用的属性(如UDISKS_PARTITION_TABLE )。

This works well on x86. 这在x86上运行良好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM