Как получать уведомления о событиях 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 messagen");
            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=%sn",
                         udev_list_entry_get_name(list_entry),
                         udev_list_entry_get_value(list_entry));
            printf("n");

}

этот код будет получать уведомления для SD-карты добавить / удалить (и начальное состояние SD - подключен/отключен). Тем не менее, это скорее Хак, и он не работает во всех случаях.

I в настоящее время используйте 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=SDx2fMMCx20x20x20x20x20x20x20x20x20x20
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=SDx2fMMCx20x20x20x20x20x20x20x20x20x20
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=SDx2fMMCx20x20x20x20x20x20x20x20x20x20
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 событие я не могу извлечь информацию о том, было ли устройство добавлено или удалено. У меня есть пытался открыть имя устройства devsdd но у меня нет разрешений, поэтому эта опция падает...

пока я просто проверяю атрибут action для разделов (add / remove ).

эта версия программы работает довольно хорошо для SD-карт, есть перегородки. Когда нет разделов, только событий.

у меня вопрос: есть ли способ проверить, был ли добавлен/удален носитель the change событие? Или есть какой-то другой способ проверить, доступно ли устройство (имея в виду проблему раздела)?

любое предложение по улучшению итерации атрибута устройства или метода получения уведомлений будет приветствоваться.

P. S. И я не могу использовать libusb :).

1 ответов


Ok. Поэтому я заставил его работать на ПК для 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 <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.