因为最终要在tiny210上实现此功能,最终选择了hotplug。
http://hi.baidu.com/hdy5200075/item/7751f48647f3d12a100ef3f6这里是hotplug检测U盘的源码,我在qt里将其写到一个hostplug.h文件里。
#ifndef HOSTPLUG_H
#define HOSTPLUG_H
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <linux/netlink.h>
#include <errno.h>
#include <fcntl.h>
static int init_hotplug_sock(void)
{
struct sockaddr_nl snl;
const int buffersize = 16 * 1024 * 1024;
int retval;
memset(&snl, 0x00, sizeof(struct sockaddr_nl));
snl.nl_family = AF_NETLINK;
snl.nl_pid = getpid();
snl.nl_groups = 1;
int hotplug_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if(hotplug_sock == -1)
{
printf("Error getting socket;%s\n", strerror(errno));
return -1;
}
/*set receive buffersize*/
setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize));
int flags=fcntl(hotplug_sock, F_GETFL,0);
fcntl(hotplug_sock, F_SETFL, flags | O_NONBLOCK);
retval = bind(hotplug_sock, (struct sockaddr*)&snl, sizeof(struct sockaddr_nl) );
if(retval < 0)
{
printf("bind failed: %s", strerror(errno));
close(hotplug_sock);
hotplug_sock = -1;
return -1;
}
return hotplug_sock;
}
#endif // HOSTPLUG_H
然后添加一个定时器,定时器时间为500ms,即每500ms扫描一次,如下:
void Widget::timerEvent(QTimerEvent *event)
{
static int n = 0;
char buf[1024] = {0}; //UEVENT_BUFFER_SIZE*2
if(event->timerId() == timer.timerId())
{
recv(hotplug_sock, &buf, sizeof(buf), 0); //use a timer to query socket from core -netlink
QString result = buf;
rectFlag = result;
qDebug()<<result;
if(result.contains("add"))
{
n++;
if (n>10)
n = 10;
ui->progressBar->setValue(n);
}
else if(result.contains("remove"))
{
n--;
if(n<0)
n = 0;
ui->progressBar->setValue(n);
}
}
else
QWidget::timerEvent(event);
}
注意这里最关键的就是
recv(hotplug_sock, &buf, sizeof(buf), 0);
这个函数,接收消息存至buf里。但默认的hotplug_sock是阻塞的,也就是当执行到recv时,程序就会停在这里,直到再次接收到内核新的消息,程序才会往下执行。为此,程序必须改动,一种思路是开一个线程,专门运行recv,停在那也无所谓;另外一种思路是将这个sock改成非阻塞的,改动部分见.h文件里画红线部分!
int flags=fcntl(hotplug_sock, F_GETFL,0);
fcntl(hotplug_sock, F_SETFL, flags | O_NONBLOCK);
当内核没有消息时,recv()之后的buf是空的。 交叉编译后,程序至Tiny210里运行良好!
上一张效果图:
但遗憾的是,工业是这么做是很不高明的,为了扫描一个U盘要开一个定时器在那扫描,因此最终采用判断/proc/scsi/usb-storage是否存在来判断u盘是否插入。曾考虑过U盘插入后,挂载点/udisk是否存在来判断。但当用户在/udisk目录下时,这时突然拔掉U盘。/udisk就会存在,而且ls查看的结果是报错。这时因为未推出U盘目录就拔掉,linux无法正常卸载造成的。当U盘插入良好时,usb-storage文件夹里会有三个文件,当卸载不成功时,会有两个文件。当卸载成功时,usb-storage这个文件夹会消失。采用像/udisk里写测试文件来判断/udisk是否可用,不可用的话就提示给用户:
bool Widget::checkSaveFile()
{
QString fileName = "/proc/scsi/usb-storage/a.txt";
QFile file(fileName);
if(!file.open(QFile::WriteOnly|QFile::Text))
return false;
else
{ file.close();
file.remove();
return true;
}
}
查询U盘状态的槽函数:
void Widget::on_queryButton_clicked()
{
QDir dir("/proc/scsi/usb-storage"); //在板子上,如果检测挂载点,改为:QDir dir("/udisk")检测挂载点
QMessageBox box;
QString mess;
box.setWindowTitle(tc->toUnicode("U盘状态"));
qDebug()<<"dir.count() = "<<dir.count();
if(rectFlag.contains("add"))
mess = tc->toUnicode("正在识别,请稍等-----");
else if(rectFlag.contains("remove"))
mess = tc->toUnicode("正在删除您的U盘-------");
else if(dir.exists())
{
if(dir.count()>2 )
{
// if(checkSaveFile())
mess = tc->toUnicode("U盘已连接!");
// else
// mess = tc->toUnicode("您的U盘已插入,但挂载点有问题,不能正常使用!建议拔掉U盘,然后重启!");
}
else if((dir.count() == 2) )
{
QDir dirMount("/udisk");
if(dirMount.exists())
{
int fd = system("umount /udisk");
qDebug()<<"fd = "<<fd;
}
else
mess = tc->toUnicode("/udisk No Exist!");
// if(checkSaveFile())
// mess = tc->toUnicode("您未插入U盘。但挂载点可写,不影响使用。如果需要请插入U盘!"); //这种情况从逻辑上讲不可能出现。
// else
// mess = tc->toUnicode("您未插入U盘。但当前挂载点有问题,建议重启后再插入U盘!");
}
else
mess = tc->toUnicode("U盘连接有故障,请重启后再插入U盘!");
}
else
mess = tc->toUnicode("U盘未连接!");
// process->start("ls /mnt\n");
//QString test = QString::number(a, 10);
// QString test = process->readAllStandardOutput();
// ui->getTextEdit->insertPlainText(test + "\n");
ui->getTextEdit->insertPlainText(mess + "\n");
autoScroll();
}
这样就能正常检测U盘了,如果想加进度条就加上。不加,也能正常检测。
问题又来了,上面采用向/udisk里写测试文件来检测/udisk是否可用,但有时用户会将U盘进行写保护。我试遍所有的方法,用open、opendir、access、stat等来检查异常情况下/udisk的属性与正常状态有何不同,最终也没查出来。也用了ls /udisk > /a.txt,截取ls的内容。但当/udisk出现异常时,报错的内容是板子上报的,并不是ls显示的内容。ls此时显示结果为空。
其实,与其判断这种误拔U盘的行为,倒不如防止。经过我研究发现,当ls出现/udisk fatal error,只要执行/umount /udisk,手动将这个文件夹卸载,再次插上U盘就可以了。为此,大家看到我上面程序里,当检测dir.count == 2时,检查/udisk是否存在,如果存在则将/udisk卸载。
这样做基本算完美解决问题了。美中不足的是,当异常出现时,如果板子程序一直在运行,则拔掉U盘再插上无事。如果此时板子重启,在板子重启前就将U盘再次插入到板子,这时候因为咱们的应用程序还未运行,还没有执行 umount /udisk,这个时候程序就检测不出来了。
要避免这个问题,就采用往U盘里写数据的方法判断,或者如果允许扫描用hostplug查询出来的信息可以得知usb的注册情况,这种思路应该也可以。一个小小的U盘检测,终于告一段落了,实现了x86下、arm平台均可用的qt检测U盘!----------------yanzi1225627
这里给一个源码资源,是老外写的,用qtcpsocket来监听netlink的消息,老外写的代码就是不一样啊,大家参考吧:
http://download.csdn.net/detail/yanzi1225627/4514740
分享到:
相关推荐
iterm2-recv-zmodem.sh
send recv nit linux 下recv send
基于ES32_SDK\Projects\ES32F36xx\Examples_ALD\UART\编写的send_recv_by_dma_fast_random_length例程,用户可以用该例程实现uart不定长的数据接收。
yog2-plugin-recv-reload ======================== 通过yog2-plugin-recv-reload,可以实现在使用YOG2框架进行开发工作时,上传APP代码无需重启服务就可以调试最新代码 系统要求 yog2-kernel >= 0.2.2 Usage 安装...
Socket下send和recv使用的说明及介绍
Qt基于tcp协议实现局域网内远程传输文件
recv一般只用在面向连接的套接字,几乎等同于recvfrom,只要将recvfrom的第五个参数设置NULL。 如果消息太大,无法完整存放在所提供的缓冲区,根据不同的套接字,多余的字节会丢弃。 假如套接字上没有消息可以读取,...
02-ER模型、三范式_recv 03-完整性_recv 04-图形界面操作_recv 05-逻辑删除_recv 06-创建表_recv 07-增加_recv 08-修改、删除_recv 09-备份与恢复_recv 10-总结_recv 2.MySQL查询 01-复习 02-查询-比较运算符 03-...
服务器远程sz 下载文件时经常会遇到这样的错误, *\*B0100000023be50; 这里是两个配置iterm2的脚本,解决上述问题
01-复习_recv 02-简介_recv 03-增、改、删_recv 04-查询_recv 05-查询2_recv 06-查询3_recv 07-聚合-分组_recv 08-聚合-筛选、排序、分页_recv 09-聚合-拆分数组_recv 资料 02.MongoDB高级 视频 01-复习 02-索引 │...
netcat0.7.1安装部署文档.。
这是套接字接收程序,还有一个发送程序tcp-send.c,学习linux可以下载看一下,对理解套接字很有用
这是一份关于 socket_recv的文档,相信对想学习 socket_recv的同学一定有很大的帮助!
Translate OpenFirmware node properties into platform data for Linux v2.13.6.
Translate OpenFirmware node properties into platform_data.
此工具可以热部署sql文件,java类,而不用重新启动服务器。具体操作方法,根据压缩包内的word文档说明进行安装!
udp recv程序,非阻塞方式。使用了udp接收ts码流,供初学者迅速掌握。并上传了makefile文件,简单修改程序可以实现简单的功能,在linux下已经测试了
gtx数据包接收模块
7-1和7-2_recv.exe