2012-06-11 16 views
6

Desidero controllare la presenza di una scheda SD e ricevere notifiche per l'aggiunta/rimozione della scheda SD.Come ricevere le notifiche per gli eventi della scheda SD?

Finora ho utilizzato libudev e ho creato una piccola applicazione che ascolta gli eventi della scheda SD.

Il codice è riportato di seguito:

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

} 

Questo codice ricevere notifiche per scheda SD aggiungere/rimuovere (e lo stato iniziale della SD - inserita/scollegato). Tuttavia, è più di un hack e non funziona in tutti i casi.

Attualmente utilizzo l'attributo ID_MODEL del dispositivo e controllo se è SD_MMC - per schede SD. Ho bisogno solo di questo tipo di carta per ora, quindi è abbastanza.

Quando si inserisce una scheda SD, vengono inviati i seguenti eventi per il blocco del sottosistema: 2 eventi change e 1 evento add per ciascuna partizione. Le proprietà degli eventi sono elencati di seguito:

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

Dall'evento change non può estrarre alcuna informazione per quanto riguarda se il dispositivo è stato aggiunto o rimosso. Ho cercato di aprire il nome del dispositivo \dev\sdd ma non avere i permessi, in modo che l'opzione cade ...

Per ora sto solo controllando l'attributo di azione per le partizioni (add/remove).

Questa versione del programma funziona piuttosto bene per le schede SD che dispongono di partizioni. Quando non ci sono partizioni, vengono ricevuti solo gli eventi change.

Quindi la mia domanda è: c'è un modo per verificare se il supporto è stato aggiunto/rimosso dall'evento change? O c'è un altro modo per verificare se un dispositivo è disponibile (tenendo presente il problema della partizione)?

Qualsiasi suggerimento su come migliorare l'iterazione dell'attributo del dispositivo o il metodo per ottenere le notifiche sarebbe gradito.

P.S. E non posso usare libusb :).

+0

non è possibile verificare se il dispositivo di blocco esiste dopo aver ottenuto un evento? – Behrooz

+0

Beh, ho provato a chiamare [open] (http://linux.die.net/man/2/open) sul valore 'DEVNAME' (' \ dev \ sdd') ma non ho i permessi, quindi la chiamata fallisce sempre. Esistono altri modi per verificare se il dispositivo esiste? –

+0

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

risposta

1

Ok. Quindi l'ho fatto funzionare su PC per schede SD senza partizioni.

Il codice aggiornato è questo:

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

} 

La soluzione (ancora non molto brillante) sta controllando per qualche attributo che è disponibile solo quando viene aggiunta la scheda SD (come UDISKS_PARTITION_TABLE).

Questo funziona bene su x86.

Problemi correlati