Comment obtenir des notifications pour les événements de carte SD?

Je veux vérifier la présence d’une carte SD et recevoir des notifications pour l’ajout / la suppression d’une carte SD.

Jusqu’à présent, j’ai utilisé libudev , et j’ai créé une petite application qui écoute les événements de carte SD.

Le code est listé ci-dessous:

 #include  #include  #include  #include  #include  #include  #include  //debug -> remove me #include  #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"); } 

Ce code recevra les notifications pour l’ajout / la suppression de la carte SD (et l’état initial du SD – branché / débranché). Cependant, il s’agit plutôt d’un hack, et cela ne fonctionne pas dans tous les cas.

J’utilise actuellement l’atsortingbut ID_MODEL du périphérique et vérifie s’il s’agit de SD_MMC – pour les cartes SD. Je n’ai besoin que de ce type de carte pour le moment, alors ça suffit.

Lorsqu’une carte SD est insérée, les événements suivants sont envoyés pour le bloc de sous-système: 2 événements de change et 1 événement d’événement pour chaque partition. Les propriétés de l’événement sont répertoriées ci-dessous:

  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  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  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 

À partir de l’événement de change , je ne peux extraire aucune information concernant le fait que le périphérique a été ajouté ou supprimé. J’ai essayé d’ouvrir le nom de périphérique \dev\sdd mais je n’ai pas les permissions, donc cette option tombe …

Pour l’instant, je vérifie juste l’atsortingbut action pour les partitions ( add / remove ).

Cette version du programme fonctionne assez bien pour les cartes SD qui ont des partitions. Lorsqu’il n’y a pas de partitions, seuls les événements de change sont reçus.

Ma question est donc la suivante : existe-t-il un moyen de vérifier si le média a été ajouté / supprimé de l’événement de change ? Ou existe-t-il un autre moyen de vérifier si un périphérique est disponible (en gardant à l’esprit le problème de partition)?

Toute suggestion d’amélioration de l’itération d’atsortingbut de périphérique ou de la méthode d’obtention des notifications serait la bienvenue.

PS Et je ne peux pas utiliser libusb :).

D’accord. Donc, je l’ai fait fonctionner sur le PC pour les cartes SD sans partitions.

Le code mis à jour est le suivant:

 #include  #include  #include  #include  #include  #include  #include  //debug -> remove me #include  #include  #include  #include  #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" // atsortingbute 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 atsortingbute 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"); } 

La solution (toujours pas très claire) vérifie certains atsortingbuts disponibles uniquement lorsque la carte SD est ajoutée (comme UDISKS_PARTITION_TABLE ).

Cela fonctionne bien sur x86.