因為最終要在tiny210上實現(xiàn)此功能,最終選擇了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); }注意這里最關(guān)鍵的就是
recv(hotplug_sock, &buf, sizeof(buf), 0);
這個函數(shù),接收消息存至buf里。但默認的hotplug_sock是阻塞的,也就是當執(zhí)行到recv時,程序就會停在這里,直到再次接收到內(nèi)核新的消息,程序才會往下執(zhí)行。為此,程序必須改動,一種思路是開一個線程,專門運行recv,停在那也無所謂;另外一種思路是將這個sock改成非阻塞的,改動部分見.h文件里畫紅線部分!
int flags=fcntl(hotplug_sock, F_GETFL,0);
fcntl(hotplug_sock, F_SETFL, flags | O_NONBLOCK);
當內(nèi)核沒有消息時,recv()之后的buf是空的。 交叉編譯后,程序至Tiny210里運行良好!
上一張效果圖:
但遺憾的是,工業(yè)是這么做是很不高明的,為了掃描一個U盤要開一個定時器在那掃描,因此最終采用判斷/proc/scsi/usb-storage是否存在來判斷u盤是否插入。曾考慮過U盤插入后,掛載點/udisk是否存在來判斷。但當用戶在/udisk目錄下時,這時突然拔掉U盤。/udisk就會存在,而且ls查看的結(jié)果是報錯。這時因為未推出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盤狀態(tài)的槽函數(shù):
void Widget::on_queryButton_clicked() { QDir dir("/proc/scsi/usb-storage"); //在板子上,如果檢測掛載點,改為:QDir dir("/udisk")檢測掛載點 QMessageBox box; QString mess; box.setWindowTitle(tc->toUnicode("U盤狀態(tài)")); 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盤!"); //這種情況從邏輯上講不可能出現(xiàn)。 // 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的屬性與正常狀態(tài)有何不同,最終也沒查出來。也用了ls /udisk > /a.txt,截取ls的內(nèi)容。但當/udisk出現(xiàn)異常時,報錯的內(nèi)容是板子上報的,并不是ls顯示的內(nèi)容。ls此時顯示結(jié)果為空。
其實,與其判斷這種誤拔U盤的行為,倒不如防止。經(jīng)過我研究發(fā)現(xiàn),當ls出現(xiàn)/udisk fatal error,只要執(zhí)行/umount /udisk,手動將這個文件夾卸載,再次插上U盤就可以了。為此,大家看到我上面程序里,當檢測dir.count == 2時,檢查/udisk是否存在,如果存在則將/udisk卸載。
這樣做基本算完美解決問題了。美中不足的是,當異常出現(xiàn)時,如果板子程序一直在運行,則拔掉U盤再插上無事。如果此時板子重啟,在板子重啟前就將U盤再次插入到板子,這時候因為咱們的應(yīng)用程序還未運行,還沒有執(zhí)行 umount /udisk,這個時候程序就檢測不出來了。
要避免這個問題,就采用往U盤里寫數(shù)據(jù)的方法判斷,或者如果允許掃描用hostplug查詢出來的信息可以得知usb的注冊情況,這種思路應(yīng)該也可以。一個小小的U盤檢測,終于告一段落了,實現(xiàn)了x86下、arm平臺均可用的qt檢測U盤!----------------yanzi1225627
這里給一個源碼資源,是老外寫的,用qtcpsocket來監(jiān)聽netlink的消息,老外寫的代碼就是不一樣啊,大家參考吧:
http://download.csdn.net/detail/yanzi1225627/4514740
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
