/*
下面是我根据网上一篇文章的思路整理的来的,以及测试验证通过,将其中的打印换成标准的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__);
}
}