2012-06-11 30 views
6

Tôi muốn kiểm tra sự hiện diện của thẻ SD và nhận thông báo để thêm/xóa thẻ SD.Cách nhận thông báo về sự kiện thẻ SD?

Cho đến nay tôi đã sử dụng libudev và tôi đã tạo một ứng dụng nhỏ để nghe các sự kiện thẻ SD.

Mã này được liệt kê dưới đây:

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

} 

Mã này sẽ nhận được thông báo cho thẻ SD thêm/xóa (và tình trạng ban đầu của SD - cắm phít cắm /). Tuy nhiên, nó là một hack, và nó không hoạt động trong mọi trường hợp.

Tôi hiện đang sử dụng thuộc tính ID_MODEL của thiết bị và kiểm tra xem đó có phải là SD_MMC - dành cho thẻ SD không. Tôi chỉ cần loại thẻ này bây giờ, vậy là đủ rồi.

Khi thẻ SD được lắp, các sự kiện sau được gửi cho khối hệ thống phụ: 2 change sự kiện và 1 add sự kiện cho từng phân vùng. Các thuộc tính sự kiện được liệt kê bên dưới:

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

Từ sự kiện change Tôi không thể trích xuất bất kỳ thông tin nào về việc thiết bị đã được thêm hoặc xóa. Tôi đã cố mở tên thiết bị \dev\sdd nhưng tôi không có quyền, do đó tùy chọn đó rơi ...

Hiện tại, tôi chỉ kiểm tra thuộc tính hành động cho các phân vùng (add/remove).

Phiên bản này của chương trình hoạt động khá tốt đối với thẻ SD có phân vùng. Khi không có phân vùng, chỉ nhận được các sự kiện change.

Vì vậy, câu hỏi của tôi là: có cách nào tôi có thể kiểm tra xem phương tiện đã được thêm/xóa khỏi sự kiện change không? Hoặc là có một số cách khác để kiểm tra xem một thiết bị có sẵn (giữ trong tâm trí các vấn đề phân vùng)?

Bất kỳ đề xuất nào về cải thiện tính lặp lại thuộc tính thiết bị hoặc phương thức nhận thông báo sẽ được hoan nghênh.

P.S. Và tôi không thể sử dụng libusb :).

+0

bạn không thể kiểm tra xem thiết bị chặn có tồn tại sau khi bạn nhận được sự kiện không? – Behrooz

+0

Vâng, tôi đã cố gắng gọi [open] (http://linux.die.net/man/2/open) trên giá trị 'DEVNAME' (' \ dev \ sdd') nhưng tôi không có quyền, vì vậy cuộc gọi luôn thất bại. Có cách nào khác để kiểm tra xem thiết bị có tồn tại không? –

+0

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

Trả lời

1

Ok. Vì vậy, tôi đã nhận nó làm việc trên máy tính cho thẻ SD mà không có phân vùng.

Mã được cập nhật là thế này:

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

} 

Giải pháp (vẫn không phải là rất tươi sáng) đang kiểm tra đối với một số thuộc tính mà chỉ có sẵn khi thẻ SD được bổ sung (như UDISKS_PARTITION_TABLE).

Tính năng này hoạt động tốt trên x86.

Các vấn đề liên quan