2012-06-11 15 views
6

Chcę sprawdzić obecność karty SD i otrzymywać powiadomienia o dodaniu/usunięciu karty SD.Jak otrzymać powiadomienia o zdarzeniach karty SD?

Do tej pory użyłem libudev i stworzyłem małą aplikację, która słucha zdarzeń karty SD.

Kod jest poniżej:

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

} 

Ten kod dostanie powiadomienia dla karty SD dodać/usunąć (i początkowy stan SD - podłączony/odłączony). Jednak jest to bardziej hack i nie działa we wszystkich przypadkach.

Obecnie używam atrybutu ID_MODEL urządzenia i sprawdzam, czy jest to SD_MMC - dla kart SD. Na razie potrzebuję tylko tego typu kart, więc wystarczy.

Po włożeniu karty SD do bloku podsystemu wysyłane są następujące zdarzenia: 2 change zdarzeń oraz 1 add zdarzeń dla każdej partycji. Właściwości zdarzeń są wymienione poniżej:

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

W przypadku change nie może uzyskać informacji o tym, czy urządzenie zostało dodane lub usunięte. Próbowałem otworzyć nazwę urządzenia \dev\sdd ale nie mam uprawnień, dzięki czemu opcja spada ...

Na razie jestem po prostu sprawdzając atrybut działania na partycjach (add/remove).

Ta wersja programu działa całkiem dobrze w przypadku kart SD z partycjami. Gdy nie ma partycji, tylko zdarzenia change są odbierane.

Moje pytanie brzmi:: czy istnieje sposób sprawdzenia, czy media zostały dodane/usunięte z wydarzenia change? Czy jest jakiś inny sposób sprawdzenia, czy urządzenie jest dostępne (pamiętając o problemie z partycją)?

Wszelkie sugestie dotyczące ulepszenia iteracji atrybutu urządzenia lub metody otrzymywania powiadomień będą mile widziane.

P.S. I nie mogę użyć libusb :).

+0

Nie możesz sprawdzić, czy urządzenie blokujące istnieje po otrzymaniu zdarzenia? – Behrooz

+0

Cóż, próbowałem wywołać [open] (http://linux.die.net/man/2/open) na wartości 'DEVNAME' (' \ dev \ sdd'), ale nie mam uprawnień, więc połączenie zawsze kończy się niepowodzeniem. Czy istnieją inne sposoby sprawdzenia, czy urządzenie istnieje? –

+0

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

Odpowiedz

1

Ok. Więc pracowałem na PC dla kart SD bez partycji.

Zaktualizowana kod jest taka:

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

} 

Rozwiązanie (nadal nie bardzo jasne) jest sprawdzenie jakiegoś atrybutu, który jest dostępny tylko wtedy, gdy jest dodana karta SD (jak UDISKS_PARTITION_TABLE).

Działa to dobrze na x86.

Powiązane problemy