Linux下C语言来检测USB设备以及自动挂载

/*
下面是我根据网上一篇文章的思路整理的来的,以及测试验证通过,将其中的打印换成标准的C打印就可以直接拿来跑。目前只是验证了U盘,没有进行硬盘测试,以及一些异常测试。但是证明整个通道是OK的。该代码的作用是:在机顶盒上检测有USB的插拔,即可通知到应用,这个不难办到,关键是插拔之后应用还要能够访问U盘目录,所以就要进行手动的mount 和umount操作。
*/

http://www.cnitblog.com/zouzheng/archive/2007/09/15/33409.html

#define FILE_DEV_CHECK             "/proc/scsi/scsi"                                          //用来检测设备数和类型
#define FILE_MOUNT_CHECK        "/proc/mounts"                                          //用来检测设备是否被mount
#define FILE_DISC_PARTS_CHECK   "/proc/partitions"                                   //用来检测设备的分区情况
#define FILE_DEV_PART_TEMPL     "/dev/scsi/host%d/bus0/target0/lun0/"   //具体的设备
#define USB_CDROM_MP                  "/tmp/cdrom"
#define USB_DISK_MP                       "/tmp/usbdisk"
#define MAX_NAME_LEN                   64
#define MAX_PART_NUM                  6   //最多运行6个分区

typedef struct s_scsi_usb_dev
{
    int type;                                        /*1 cdrom 2 disk */
    int index;                                       /*like host0 host1*/
    char file_statu[MAX_NAME_LEN];                    /*like "/proc/scsi/usb-storage-%d/%d"*/
    char devfile_parts[MAX_PART_NUM][MAX_NAME_LEN];   /*存储每个分区的设备文件*/
    char mount_path[MAX_PART_NUM][MAX_NAME_LEN];      /*与上面对应的mount点*/
    int part_num;                                    //4分区数
    struct s_scsi_usb_dev *next_dev;                 //4指向下一个设备
} SCSI_USB_DEV;

static SCSI_USB_DEV *f_first_dev = NULL;
static BOOL insert = FALSE;
static BOOL find_attached = FALSE;
static int is_manual_umount = 1;

static void CLEAR_DEV(void);
static int CHECK_PARTS(SCSI_USB_DEV *dev);
static int ADD_DEV(SCSI_USB_DEV *dev);
static int INIT_DEV(SCSI_USB_DEV *dev, int index, char *type);
static int check_mount(SCSI_USB_DEV *dev);
static int do_mount(SCSI_USB_DEV *dev);
static int do_umount(SCSI_USB_DEV *dev);
static int process_dev(SCSI_USB_DEV *dev);
static int find_device(void);


//device opt function
static int ADD_DEV(SCSI_USB_DEV *dev)
{
    if(f_first_dev)
    {
        dev->next_dev = f_first_dev;
        f_first_dev = dev;
    }
    else
    {
        f_first_dev = dev;
        dev->next_dev = NULL;
    }
    return KK_FS_OK;
}

static int INIT_DEV(SCSI_USB_DEV *dev, int index, char *type)
{
    dev->index = index;

    if (!strncmp(type, "CD-ROM", 6))//cd-rom
    {
        KK_INFO("CD-ROM\n");
        dev->type = 1;
        dev->part_num = 1;
        sprintf(dev->devfile_parts[0], FILE_DEV_PART_TEMPL"cd", index);
        strcpy(dev->mount_path[0],USB_CDROM_MP);
        KK_INFO("---%d---,%s,%s\n", dev->index, dev->devfile_parts[0],dev->mount_path[0]);
    }
    else//usb disk
    {
        KK_INFO("usb-storage\n");
        dev->type = 2;
        dev->part_num = CHECK_PARTS(dev);
    }
    return KK_FS_OK;
}

/*
* 如果在/proc/scsi/scsi/中发现的设备是usb-storage,则进一步在/proc/partitions/中分析其所
* 包含几个分区,并将其记录下来dev->devfile_parts,mount路径也一并设置dev->mount_path
* 例如发现一个U盘,仅仅有一个分区,系统将其挂载在/dev/sda1/下,则可以将其mount路径设置成
* 用户可以访问的/tmp/usb/disk0/part0/下
*/
static int CHECK_PARTS(SCSI_USB_DEV *dev)
{
    int len = 0;
    int part_num = 0;
    char buf[1024] = {0};     //1024 is enough for save information of FILE partitions
    char hoststr[16] = {0};
    char *delim="\n";
    char *line = NULL;
    char *strtok_tmp_ptr = NULL;
    char *seek = NULL;
    char *seek_sd = NULL;      //USED FOR DEVICE MOUNTED ON SD*
    char *seek_hd = NULL;      //USED FOR DEVICE MOUNTED ON HD*
    char *part_blocks = NULL;
    FILE *fd = NULL;

    fd = fopen(FILE_DISC_PARTS_CHECK, "r"); //直接操作open打开系统文件
    if(fd != NULL)
    {
        KK_INFO("[%s]open file %s success\n",__FUNCTION__,FILE_DISC_PARTS_CHECK);
        len = fread(buf, 1, sizeof(buf), fd);
        fclose(fd);
        if(len > 0)
        {
            line = strtok_r(buf, delim, &strtok_tmp_ptr);//以"\n"结束符来截断字符串
            while(line)
            {
                seek_sd = strstr(line, "sd");//成功找到sd后指针seek指向s, seek_sd[0] = 's', seek_sd[1] = 'd'
                seek_hd = strstr(line, "hd");//成功找到hd后指针seek指向h, seek_hd[0] = 'h', seek_hd[1] = 'd'

                if (seek_sd)
                {
                    if(seek_sd[2] >= 'a' && seek_sd[2] <= 'z')
                    {
                        if(seek_sd[3] >= '1' && seek_sd[3] < '9')
                        {                    
                            KK_INFO("[%s]find device %s\n",seek_sd);
                            sprintf(dev->devfile_parts[part_num],"/dev/%s", seek_sd);
                            sprintf(dev->mount_path[part_num], USB_DISK_MP"/disk%d/part%d",dev->index, part_num);
                            part_num ++;
                            if (part_num == MAX_PART_NUM)
                            {
                                 break;//too many parts ignore
                            }
                        }
                    }
                }
                if (seek_hd)
                {
                    if(seek_hd[2] >= 'a' && seek_hd[2] <= 'z')
                    {
                        if(seek_hd[3] >= '1' && seek_hd[3] < '9')
                        {
                            KK_INFO("[%s]find device %s\n",seek_sd);
                            sprintf(dev->devfile_parts[part_num],"/dev/%s", seek_hd);
                            sprintf(dev->mount_path[part_num], USB_DISK_MP"/disk%d/part%d",dev->index, part_num);
                            part_num ++;
                            if (part_num == MAX_PART_NUM)
                            {
                                 break;//too many parts ignore
                            }
                        }
                    }
                }
                line = strtok_r(NULL, delim, &strtok_tmp_ptr);
            }
        }
    }
    else
    {
        perror(FILE_DISC_PARTS_CHECK);
    }
    return part_num;
}

/*
* 线程扫描时会不停的调用该接口去清除之前获取的设备信息
*/
static void CLEAR_DEV(void)
{
    SCSI_USB_DEV *cur_dev = f_first_dev;
    SCSI_USB_DEV *tmp_dev;
    while (cur_dev)
    {
        tmp_dev = cur_dev;
        cur_dev = cur_dev->next_dev;
        free(tmp_dev);
    }
    f_first_dev = NULL;
}


/*
* 打开"/proc/scsi/scsi" 里面会有如下信息,该接口通过轮询Host: scsi去查找有无插上设备
* 以及轮询Type 去查询设备类型
* Attached devices:
*
* Host: scsi0 Channel: 00 Id: 00 Lun: 00
*    Vendor: BENQ        Model: DVD-ROM 16X      Rev: A.DD
*    Type:   CD-ROM                           ANSI SCSI revision: 02
*
* Host: scsi1 Channel: 00 Id: 00 Lun: 00
*    Vendor: FUJITSU     Model: MHT2040AH         Rev: 0000
*    Type:   Direct-Access                    ANSI SCSI revision: 02
*
*
*/
static int find_device()
{
    FILE *fd = NULL;
    int len = 0;
    int dev_num = 0;
    char buf[1024] = {0};
    char *seek = NULL;
    SCSI_USB_DEV *new_dev = NULL;

    //clear exist device
    CLEAR_DEV();

    //add new device
    fd = fopen(FILE_DEV_CHECK, "r");//直接操作open系统文件/proc/scsi/scsi

    if(fd > 0)
    {
        /*
        * 如果没有任何设备接入,那么/proc/scsi/scsi中仅仅包含下列字符 "Attached devices:"
        * 文件长度为18。所以下面以20个字符来做判断。
        */
        len = fread(buf, 1, sizeof(buf), fd);
        fclose(fd);
        if(len < 20 && find_attached == TRUE)
        {
            find_attached = FALSE;
            KK_ERROR("Device removed......\n");            
            KKFS_Callback(KK_FS_DISK_REMOVED);
            return KK_FS_OK;
        }
        if (len > 0)
        {
            /*
            * 如果文件打开成功,而且长度大于20,表示里面是有设备记录的,如果find_attached标志之前是FALSE
            * 表示有新的动作:设备插入系统
            */
            if(len > 20 && find_attached == FALSE)
            {
                find_attached = TRUE;
                KK_ERROR("Device Inserted......\n");            
                KKFS_Callback(KK_FS_DISK_INSERT);
            }
            seek = buf;
            while(seek && ((strlen(seek)) > 0))//jacky
            {
                seek = strstr(seek, "Host: scsi");//从seek 中查找"Host: scsi"的第一次出现,返回第一次出现的位置
                if(seek && (seek[0] == 'H'))
                {
                    seek += strlen( "Host: scsi");
                    seek = strstr(seek, "Type:");
                    if(seek && (seek[0] == 'T'))
                    {
                        seek += strlen("Type:");
                        while(*seek == ' ')
                        {
                            seek++;
                        }
                        new_dev = malloc(sizeof(SCSI_USB_DEV));

                        INIT_DEV(new_dev, dev_num, seek);
                        ADD_DEV(new_dev);      
                        dev_num ++;
                    }
                }
            }
            KK_INFO("dev_num = %d\n", dev_num);
        }
    }
    else
    {
        perror(FILE_DEV_CHECK);
    }
    return dev_num;
}


static int check_mount(SCSI_USB_DEV *dev)
{
    FILE *fd = NULL;
    int len = 0;
    int i = 0;
    char buf[1024] = {0};
    char *seek = NULL;

    fd = fopen(FILE_MOUNT_CHECK, "r");
    if(fd > 0)
    {
        KK_INFO("[%s]open file %s success\n",__FUNCTION__, FILE_MOUNT_CHECK);

        len = fread(buf, 1, sizeof(buf), fd);
        fclose(fd);
        if (len > 0)
        {
            buf[len] = '\0';
            if(dev->type == 2 && dev->part_num > 1) 
            {
                i ++;   /*if disk ignore first part disc*/
            }
            for(; i < dev->part_num; i++)
            {
                seek = strstr(buf, dev->devfile_parts[i]);
                if (seek != NULL)
                {
                    return KK_FS_OK;/*have one part mounted return 1*/
                }
            }
        }
    }
    else
    {
        perror(FILE_MOUNT_CHECK);
    }
    return KK_FS_FAIL;
}


//mount format: # mount -t vfat /dev/sda1 /1 
/*
* # cat /proc/mounts 
* rootfs / rootfs rw 0 0
* proc /proc proc rw 0 0
* devpts /dev/pts devpts rw,mode=600 0 0
* sysfs /sys sysfs rw 0 0
* /dev/mtdblock3 /usr/sbin/zoran squashfs ro 0 0
* /dev/sda1 /tmp/usbdisk/disk0/part0 vfat rw,fmask=0022,dmask=0022,codepage=cp437,iocharset=iso8859-1 0 0
* /dev/sdb1 /tmp/usbdisk/disk0/part0 vfat rw,fmask=0022,dmask=0022,codepage=cp437,iocharset=iso8859-1 0 0
* /dev/sdc1 /tmp/usbdisk/disk0/part0 vfat rw,fmask=0022,dmask=0022,codepage=cp437,iocharset=iso8859-1 0 0
*/
static int do_mount(SCSI_USB_DEV *dev)
{
    int i              = 0;
    int is_vcd         = 0;
    int mount_ok       = 0;
    char fstype[10]     = {0};
    char tmpdir[50]     = {0};
    char check_cmd[50] = {0};
    char mount_data[30] = {0};
    char mount_cmd[64] = {0};
    unsigned long mountflags = 0;

    /*
    * CD-ROM这里可以不去考虑,不论其完善与否,可以作为扩展放在这里
    */
    if(dev->type == 1 && is_manual_umount == 0)
    {
        strcpy(fstype, "iso9660");
        mountflags= 0xc0ed0000 | 1 ;
        strcpy(mount_data,"codepage=936,iocharset=gb2312");
        if (mount(dev->devfile_parts[0], dev->mount_path[0], fstype ,mountflags, mount_data)== 0)
        {
            mount_ok = 1;
            KK_INFO("mount -t %s %s %s success\n", fstype, dev->devfile_parts[0], dev->mount_path[0]);
            //check is vcd
            sprintf(check_cmd,"ls %s/vcd/*.vcd", dev->mount_path[0]);
            is_vcd = (system(check_cmd) == 0);
            if (is_vcd)
            {
                if (umount(dev->mount_path[0]) == 0)
                {
                    KK_INFO("umount %s success(vcd iso9660)\n", dev->devfile_parts[0]);
                }
                else
                {
                    is_vcd = 0;
                    KK_INFO("umount %s failed (vcd iso9660)\n", dev->devfile_parts[0]);
                }
            }
        }
        else
        {
             mount_ok = 0;
        }

        if (mount_ok == 0 || is_vcd)
        {
            KK_INFO("mount -t %s %s %s failed, try cdfs\n", fstype, dev->devfile_parts[0], dev->mount_path[0]);
            strcpy(fstype, "cdfs");
            if (mount(dev->devfile_parts[0], dev->mount_path[0], fstype ,mountflags, mount_data)== 0)
            {
                KK_INFO("mount -t %s %s %s success\n", fstype, dev->devfile_parts[0], dev->mount_path[0]);
                return KK_FS_OK;
            }
            else
            {
                KK_INFO("mount -t %s %s %s failed, try cdfs\n", fstype, dev->devfile_parts[0], dev->mount_path[0]);
                return KK_FS_FAIL;
            }
        }
    }
    else if (dev->type == 2)
    {
        sprintf(tmpdir, USB_DISK_MP"/disk%d", dev->index);
        mkdir(tmpdir, 0777);
        strcpy(fstype, "vfat");
        mountflags= 0xc0ed0000;
        strcpy(mount_data,"codepage=936,iocharset=gb2312");

        if (dev->part_num > 1)
        {
             i ++; /*if disk ignore first part disc*/
        }
        for(; i < dev->part_num; i++)
        {
            mkdir(dev->mount_path[i], 0777);
            sprintf(mount_cmd, "mount -t vfat %s %s", dev->devfile_parts[i], dev->mount_path[i]);
            system(mount_cmd);
        }
    }
    else
    {
        return KK_FS_FAIL;
    }
    return KK_FS_OK;
}

static int do_umount(SCSI_USB_DEV *dev)
{
    int i = 0;
    char tmpdir[50] = {0};

    if (dev->type == 1)//cdrom
    {
        if (umount(dev->mount_path[0]) == 0)
        {
            KK_INFO("umount %s success\n", dev->devfile_parts[0]);
            return KK_FS_OK;
        }
        else
        {
            KK_INFO("umount %s failed\n", dev->devfile_parts[0]);
            return KK_FS_FAIL;
        }
    }
    else if(dev->type == 2)//usb-storage
    {
        if (dev->part_num > 1)
        {
            i ++; /*if disk ignore first part disc*/
        }

        for(; i < dev->part_num; i++)
        {
            if (umount(dev->mount_path[i]) == 0)//注意不是umount /dev/sda1 而是umount /tmp/disk/ 之类的用户能访问的路径
            {
                KK_INFO("umount %s success\n", dev->mount_path[i]);
                remove(dev->mount_path[i]);
            }
            else
            {
                KK_INFO("umount %s failed\n", dev->mount_path[i]);
            }
        }
        sprintf(tmpdir, USB_DISK_MP"/disk%d", dev->index);
        remove(tmpdir);
    }
    return KK_FS_OK;
}


static int process_dev(SCSI_USB_DEV * dev)
{
    if (find_attached == TRUE)//检测设备是否插上
    {
        if (check_mount(dev) == KK_FS_FAIL)//检测设备是否mount上
        {
            KKFS_Callback(KK_FS_Partition_MOUNT_ERROR);
            do_mount(dev);
            KKFS_Callback(KK_FS_Partition_MOUNT_OK);
        }
        else
        {
            if(insert == FALSE)
            {
                insert = TRUE;
                KKFS_Callback(KK_FS_Partition_MOUNT_OK);
            }
        }
    }
    else//如果是已经removed device 就直接将其umount掉
    {
        if (check_mount(dev) == KK_FS_OK)
        {
             do_umount(dev);
        }
        insert = FALSE;
    }
    return KK_FS_OK;
}

static void thread_for_usb()
{
    SCSI_USB_DEV *cur_dev = NULL;
    mkdir(USB_DISK_MP, 0777);
    mkdir(USB_CDROM_MP, 0777);

    while(1)
    {
        find_device();//查找设备 初始化设备列表        
        cur_dev = f_first_dev;
        while(cur_dev)
        {
            process_dev(cur_dev);//对每个设备进行处理
            cur_dev = cur_dev->next_dev;
        }
        sleep(3);   // 3秒钟检测一次        
    }
}

extern void check_usb_thread()
{
    pthread_t thread_id;
    if (0 != pthread_create(&thread_id, NULL, (void *)thread_for_usb, NULL))
    {
        KK_ERROR("[%s] Thread create failed\n",__FUNCTION__);
    }
}

展开阅读全文

没有更多推荐了,返回首页