NL80211使用笔记

最近在做一个项目,需要通过NL80211和驱动打交道。

以前没用过,走了不少弯路,因此做个笔记,方便自己,当然如果能够帮助别人更好。

无线网卡驱动主要有两个“标准”(这样的形容并不准确!):

  • WEXT(Wireless Extension):使用WEXT的工具通过ioctl和驱动通信,典型工具ifconfig等;
  • NL80211(Netlink 80211):使用NL80211的工具通过一个特殊的socket和驱动打通信,典型工具包括IW、iwconfig等。

这里的NL80211仅仅是netlink工具在无线驱动方面的一个应用,其实它的应用很广泛,也很基础。Netlink提供了一种通信方式,通信双方可以是用户态或内核态,关于这方面的介绍,直接看libnl(netlink的一个实现)的网站(http://www.infradead.org/~tgr/libnl/),讲解的很详细。目前使用netlink主要是通过libnl来做,当然也可以自己拼凑和解析消息。

这里提供一个我编译的libnl动态库,使用的是Google提供的ANDROID-NDK-R7,理论上可用于android 3.2以上的系统。http://115.com/file/e789tu6y#libnltest.so

下面是我使用的一些代码,是安卓的JNI接口,用于在连接上AP后获取信号强度。主要参考了iw和wpa_supplicant的相关实现。

int GetSignalPower(const unsigned char *addr, char *signal)    // 消息函数
{
struct nl_sock *l_nl_handle = NULL;
struct nl_cache *l_nl_cache = NULL;
struct genl_family *l_nl_family = NULL;
struct nl_cb *l_nl_cb = NULL;
int ret = 0;
int err = 1;
struct nl_msg *l_msg = NULL;
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Enter GetSignalPower”);
if(signal == NULL)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to get signal power, bad params.”);
return -1;
}
*signal = 0;
//char signal = 0;
//unsigned char addr[6] = {0×00, 0x1D, 0x0F, 0x3F, 0×43, 0x3E};
l_nl_cb = nl_cb_alloc(NL_CB_DEFAULT);    // 创建回调
if(l_nl_cb == NULL)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to alloc cb.”);
printf(“Failed to alloc cb.\n”);
ret = -1;
goto error_catch;
}
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to alloc callback.”);
l_nl_handle = nl_socket_alloc_cb(l_nl_cb);    // 根据回调创建netlink socket
if(l_nl_handle == NULL)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to alloc a netlink socket.”);
printf(“Failed to alloc a netlink socket.\n”);
ret = -1;
goto error_catch;
}
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to alloc socket.”);

ret = genl_connect(l_nl_handle);    // 连接内核
if(ret != 0)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to connect to generic socket, error code is %d.\n”, ret);
printf(“Failed to connect to generic socket, error code is %d.\n”, ret);
goto error_catch;
}

__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to connect generic socket.”);

ret = genl_ctrl_alloc_cache(l_nl_handle, &l_nl_cache);    // 创建cache,我也不清楚这个操作必要性
if(ret < 0)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to alloc cache.”);
printf(“Failed to alloc cache.\n”);
goto error_catch;
}
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to genl_ctrl_alloc_cache.”);
l_nl_family = genl_ctrl_search_by_name(l_nl_cache, “nl80211″);    // 查找NL80211簇
if (l_nl_family == NULL)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “nl80211: ‘nl80211′ generic netlink not found.”);
printf(“nl80211: ‘nl80211′ generic netlink not found.\n”);
ret = -1;
goto error_catch;
}
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to genl_ctrl_search_by_name.”);

l_msg = nlmsg_alloc();    // 创建netlink message
if (l_msg == NULL)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to alloc message.”);
printf(“Failed to alloc message.\n”);
ret = -1;
goto error_catch;
}
__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Succeed to nlmsg_alloc.”);
genlmsg_put(l_msg, 0, 0, genl_family_get_id(l_nl_family), 0,
0, NL80211_CMD_GET_STATION, 0);    // 填充消息,这里的“NL80211_CMD_GET_STATION”是一条NL80211的命令,具体作用参考nl80211.h
nla_put(l_msg, NL80211_ATTR_MAC, ETH_ALEN, addr);    // 填充消息,这里的MAC地址是AP的MAC
nla_put_u32(l_msg, NL80211_ATTR_IFINDEX, if_nametoindex(“wlan0″));    // 填充消息,很明显,这里指定了要查询的网卡

nl_cb_set(l_nl_cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);    // 设置回调,这里的“NL_CB_FINISH”等是libnl定义的一些回调类型,Google一下就知道用处和用法
nl_cb_set(l_nl_cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
nl_cb_err(l_nl_cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(l_nl_cb, NL_CB_VALID, NL_CB_CUSTOM, get_sta_handler, signal);

ret = nl_send_auto_complete(l_nl_handle, l_msg);    // 发送消息
if (ret < 0)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to send msg.”);
printf(“Failed to send msg.\n”);
ret = -1;
goto error_catch;
}

do{
nl_recvmsgs(l_nl_handle, l_nl_cb);    // 注意!这里要重复读取直到我们的回调函数被调用
sleep(1);
}while(err>0);

error_catch:    // 一些释放工作
if(l_nl_family != NULL)
{
genl_family_put(l_nl_family);
l_nl_family = NULL;
}
if(l_nl_cache != NULL)
{
nl_cache_free(l_nl_cache);
l_nl_cache = NULL;
}
if(l_nl_handle != NULL)
{
nl_socket_free(l_nl_handle);
l_nl_handle = NULL;
}
if(l_nl_cb != NULL)
{
nl_cb_put(l_nl_cb);
l_nl_cb = NULL;
}
if(l_msg != NULL)
{
nlmsg_free(l_msg);
l_msg = NULL;
}

__android_log_print(ANDROID_LOG_DEBUG, NL80211_LOG, “Exit GetSignalPower”);

if(*signal == 0)
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “signal 0.”);
return -1;
}
else
{
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “signal %d.”, *signal);
return 0;
}
}

static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)    // 错误回调函数
{
int *ret = (int *)arg;
*ret = err->error;
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “nl80211 Error handle!”);
printf(“Error handle\n”);
return NL_SKIP;
}

static int finish_handler(struct nl_msg *msg, void *arg)    // 结束回调函数
{
int *ret = (int *)arg;
*ret = 0;
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “nl80211 Finish handle!”);
printf(“Finish!\n”);
return NL_SKIP;
}

static int ack_handler(struct nl_msg *msg, void *arg)    // 确认回调函数
{
int *ret = (int *)arg;
*ret = 0;
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “nl80211 ACK handle!”);
printf(“ACK!\n”);
return NL_STOP;
}

static int get_sta_handler(struct nl_msg *msg, void *arg)    // 这里是我们真正读取消息的地方!!
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct nlattr *tb_nested[NL80211_STA_INFO_MAX + 1];
struct genlmsghdr *gnlh = (struct genlmsghdr *)nlmsg_data(nlmsg_hdr(msg));
struct _ap_status *data = (struct _ap_status *)arg;
char signal;

if( 0 != nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL))    // 解析消息,第一次解析,这里的“NL80211_ATTR_MAX”等的用处和用法参考nl80211.h
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to parse nl attrs.!”);
printf(“Failed to parse nl attrs.\n”);
return NL_SKIP;
}
else
{
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “Succeeded to parse nl attrs!”);
printf(“Succeeded to parse nl attrs.\n”);
}

if (tb[NL80211_ATTR_STA_INFO])
{
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “We have NL80211_ATTR_STA_INFO!”);
printf(“We have NL80211_ATTR_STA_INFO\n”);
if(nla_parse_nested(tb_nested, NL80211_STA_INFO_MAX, tb[NL80211_ATTR_STA_INFO],  NULL) != 0)    // 解析消息,第二次解析
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “Failed to parse nl attrs nested.”);

printf(“Failed to parse nl attrs nested.\n”);
return NL_SKIP;
}
else
{
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “Succeeded to parse attrs nested.”);
printf(“Succeeded to parse attrs nested.\n”);
}
if(tb_nested[NL80211_STA_INFO_SIGNAL])
{
signal = nla_get_u8(tb_nested[NL80211_STA_INFO_SIGNAL]);
if(arg != NULL)
{
(*(char *)arg) = signal;
}
__android_log_print(ANDROID_LOG_INFO, NL80211_LOG, “ap signal is %d\n”, *(char *)arg);
printf(“ap signal is %d\n”, signal);
}
//        if(tb_nested[NL80211_STA_INFO_SIGNAL_AVG])
//        {
//            signal = nla_get_u8(tb_nested[NL80211_STA_INFO_SIGNAL_AVG]);
//            printf(“ap avg signal is %d\n”, signal);
//        }
}
else
{
__android_log_print(ANDROID_LOG_ERROR, NL80211_LOG, “No NL80211_ATTR_STA_INFO.”);
printf(“No NL80211_ATTR_STA_INFO\n”);
}

return NL_SKIP;
}

 

Posted in Linux | Tagged , | Leave a comment

饭否WebOS客户端开发

目前客户端的开发还处在很原始的阶段,仅仅是打通了网络交互的通道,还有很多工作要做。不过多亏了JavaScript这个语言本身的易用性,省去了很多内存调试的痛苦,可以专心在业务的实现上。

项目:https://github.com/sqbing/Fanfou-Client-For-WebOS

下面列出的是使用部分技术:

  • XAUTH认证

XAUTH是OAUTH的简化版。没有选择OAUTH是因为不想服务端的开发分散我的注意力,饭否的XAUTH API认证不象某围脖那样苛刻(居然还要企业注册和个人实名!!!),而且从API申请到确认,前后只花了不到一天的时间,rex朋友相当敬业。

  • JQuery库

WebOS SDK自带的AJAX方法不好用,当然也可能是我的水平问题,没有正确使用。我下载了JQuery 1.7来做AJAX,其实JQ的强大远不止这么简单。

已经完成:
1.XAUTH认证
2.更新状态
3.上传照片
4.通过Just Type更新状态
5.退出登录

计划中:
0.App Menu中的“首选项”
1.“首页”scene
2.“@”scene
3.“私信”scene

=====================2012-04-18=======================

由于个人能力有限,对UI没有开发经验,饭否的客户端暂时停止更新,浏览的部分待以后有机会继续更新。

Posted in 开发 | Tagged , | 2 Comments

Windows Cygwin NFS配置方法

微软为Windows使用NFS提供了一个服务安装包,但是200MB的大小和繁琐的安装步骤把我吓到了,要知道Linix下的NFS配置只需要修改一个简单的配置文件,几个字母就能搞定!
果断使用Cygwin来配置,下面是我的配置过程:
首先安装软件包:vim,nfs-server,sunrpc,cygrunsrv
安装完成后,编辑/etc/exports,加入要分享的目录,比如:
/cygdrive/d/WLAN *(ro,no_root_squash)
最后打开"管理",找到服务"Cygwin portmap”重启即可。

在此再次对微软的夸张配置步骤表示不解。

Posted in Linux | Tagged , | Leave a comment

硬盘安装Linux Mint Debian

首先为计算机安装GRUB,然后将下载的Linux Mint的安装镜像解压到某一个分区,最好是根分区,我没试过非根分区会不会出现问题。

GRUB的命令不赘述了,Google上一大把。重启进入GRUB命令行之后:

root (hd0,4)
kernel /casper/vmlinuz boot=live live-media-path=/casper
initrd /casper/initrd.lz
boot

然后系统会live启动,进入桌面。

这个时候需要进入命令行,设置root用户的密码,Crtl + Alt +F1

root(以root用户登录,此时没有密码)
passwd root
(此时输入密码,2次)

然后Crtl + Alt +F7重新进入图形界面,启动终端:

su
(输入root密码)
live-installer

Posted in Linux | Tagged , , | Leave a comment

国丑这个事,不能说的太细

今早正整理文档,忽然听到防空警报,同事说,到10点了,我赶紧对对手表。不知道这警报会不会吵醒因为昨天淘宝双12太兴奋失眠的朋友。

中午热饭的时候,开手机看人人,饭否。 果然,一片“哀嚎之声”,要是人人可以发语音状态,说不定还真有人对着手机麦克风录哭腔,夜里走路连续播放还能防狼。

咱们五千年文明传承下来一句话,“家丑不可外扬”。丢人得丢在家里头,不能让外面人知道,不然会被笑话。家里头人多了,可能是族丑,族里头人多了,就是国丑。可是再丑也不会是耻,耻得是别人欺负到咱头上,那算耻,那得找天打还回去。什么开坦克压马路啦 什么埋车皮啦, 什么送校车啦,只能是国丑,不好说是国耻。

我这人不爱看历史书,那些四位数三位数的年代和事件真的很难联系起来,再加上中学历史书讲的东西很多没有逻辑性,更让我头大。不过有个事我记得,元清两代,统治者不是汉人,取得政权的过程也不是和谈,那是抢下来的,屠城的事,能少的了嘛,这国耻从来没人提过嘛。不过这样算的话,恐怕每天都得听警报声,考虑到咱们财政紧张,很多地方的钱只够盖行政大楼和福利房,连能撑到70年的普通住房都盖不起,省下这电也算是为民着想,深谋远虑。

至于国丑,这事不能说的太细。 我只知道,正面国丑的国家才是被世界尊重的国家。

Posted in | Leave a comment

编译GCC-3.4.6

由于项目需要这个版本的GCC,但是Debian Testing里又没有这个包,不得不自己编译…… 我的host

Linux 3.1.0-1-686-pae i686 GNU/Linux

记得先根据自己的发行版设置相应的环境变量,免得GCC找不到相应的头文件和库。

export C_INCLUDE_PATH=”/usr/include/i386-linux-gnu/” export CPLUS_INCLUDE_PATH=”/usr/include/i386-linux-gnu/” export LD_LIBRARY_PATH=”/usr/lib/i386-linux-gnu” export LIBRARY_PATH=”/usr/lib/i386-linux-gnu”

从GNU的镜像站点下载了gcc-3.4.6.tar.gz

tar -xvaf gcc-3.4.6.tar.gz
./configure
make

出错是正常的,不出错就不是Linux了。

错误#1:

objc/objc-parse.y:1702.19-20: $$ for the midrule at $4 of `structsp_attr’ has no declared type

http://blog.gmane.org/gmane.comp.parsers.bison.general/month=20090301/page=1介绍,这是bison解析objc-parse.y中的$$时出现的问题,只要根据错误提示修改对应的$$为$<ttype>$就行。

错误#2:

gnu/stubs-32.h no such file or directory.

这个问题很奇怪,因为我的/usr/include/gnu目录下没有stubs-32.h这个头文件,但是在/usr/include/i386-linux-gnu/gnu目录下有这个文件。我找了很久都没有找到解决办法,我就用了比较流氓的办法,直接修改头文件stubs.h。

# include <gnu/stubs-32.h>

改为

#include <../i386-linux-gnu/gnu/stubs-32.h>

再make就不会报错了。

错误#3:

bits/predefs.h no such file or directory.

事实上这个bits文件夹在/usr/include/i386-linux-gnu/下面,需要为这个文件夹建立一个软链接,像这样:

sudo ln -s /usr/include/i386-linux-gnu/bits /usr/include/bits

除了bits文件夹外,sys和gnu文件夹也要做相同的处理。

Posted in Linux | Leave a comment

找回Empathy中消失的MSN

最近升级Debian的testing来了一次大升级,我也顺势把服役多时的Pidgin换成了Empathy。
可是可是……怎么MSN没啦?Empathy没道理不支持MSN的协议。
Google说,这个可以好办,装telepathy-butterfly这个包就行。
果然,MSN回来了。
如果你想用AOL等等,可以试试telepathy-haze这个包,瞬间多出来一大串的帐号类型。

Posted in 未分类 | Tagged , , | 2 Comments

E: Broken package的一个解决办法

卸载qt开发套件时,不小心连带x的包也删掉了,startx的时候找不到/usr/bin/X……
想再安装回来的时候,apt反复报依赖问题。
试过
apt-get install xserver-xorg -f

dpkg –configure -a
都不能解决问题。
Google说,可能是source.list出了问题,需要移除第三方源,重新update一次就能解决。
我的发行版是Linux Mint Debian,source.list除了第一条都是Debian的官方源,果断注释第一条,接着apt-get update,再apt-get install xserver-xorg -f,依赖问题没有了,哈哈!

Posted in Linux | Tagged , , | Leave a comment

BREW小结(未完)

最近接了一个BREW的项目,做简单的应用开发。接触一个新平台从有些磕磕绊绊,今天给自己做个小结。

2011
BREW的全称,历史之类的东西就不列出来了,我想高通和Wiki一定概括的比我好。我的开发平台是BREW SDK 3.1.5 SP02,用近乎标准的C编写,使用ADS1.2提供的ARM编译器和链接器,最后的成品不是elf而是mod,使用高通提供的elf2mod工具。

就像我说的,我用的C近乎标准,因为高通对一些标准函数做了封装,我通过宏访问,而不是直接调用。举个例子,我使用FREE而不是free,使用MALLOC而不是malloc。为了啥?“跨版本”呗!封装还能有别的用吗……另外,高通还用自己的方式模拟了类的行为,包括继承和多态等。实际上就是虚函数表,这个从示例程序里能很容易看出来。这些“伪类”还有一个引用计数一样的变量,当没有人再引用这个类时,他会主动销毁。QueryInterface也很有意思,实际上就是强制类型转换,但是比较优雅。

好了,说了一些很容易看出来的BREW feature,该讲一讲真正有用的东西了。就像我一个朋友说的,严谨的公司不一定有严谨的文档。BREW的文档并不算丰富,仅仅是简单的功能说明,大多没有例子,需要自己尝试或者在论坛求助。我想,新入门的开发者(像我这样),大多数的时间就花在这里头了。接下来要说的,都是我在实践中总结的东西,如果有问题,而且正巧被正在看文档的你发现了,请告诉我。

首先我要讲的是音频流的播放和下载,通俗讲就是边下边播。这种情况下,务必用PCM编码的.wav格式,其他格式我不保证能够实现,特别是MP3,特别不保证……我的做法,其实也是网络上能够找到的做法之一,创建一个IFIFO,作为播放的源。然后创建一个IMEDIA,根据.wav文件的实际情况编辑参数,如采样频率(8kHz/16kHz/…),宽度(8bit/16bit/…)等,用SetMediaDataEx配置IMEDIA,这时IMEDIA应处在MM_STATE_IDLE或者MM_STATE_READY状态。再说下载,模仿高通官网上给的例子程序,创建IWeb,设置Option和回调,调用IWeb_GetResponse,在回调函数中获取source,然后重复的对source做read操作,根据read的返回值决定是保存数据还是设置回调,等待下次回调。再说说播放那边,我的IMEDIA有源IFIFO了,但是IFIFO里还没有数据,我要做的就是不断往里头塞数据,并且在我认为缓存足够播放的时候调用MEDIA_Play。有一点需要注意,要不断检查IFIFO的使用量,用IFIFO_GetUsed,因为,如果你的下载速度不够快,即IFIFO塞数据的速度不够快,IMEDIA读完了IFIFO里的内容,读到了ISOURCE_Wait,那么前面做的工作都白费了,你会发现IMEDIA会停留在MM_STATE_READY状态,Ticktime还在不断更新,但是就是不读IFIFO了,暂停再恢复也无济于事。IMEDIA在处理“源数据”的时候,不会Readable,而是直接卡住,不再读。塞数据的时机根据情况来定,可以选择在下载中,保存数据的时候顺便塞给IFIFO,也可以开启一个新线程,不断读下载的数据,并塞给IFIFO。

//================看来我这两天得到的经验不少,暂时写不完,先就此搁笔,过两天有时间再续。===============

2012-2-6

写不出来了,几个月不碰BREW的东西。

剧终。

Posted in BREW | Tagged | Leave a comment

Unknown media type in type ‘all/all’ 的解决办法

在我的Mint Debian上,安装KDE桌面环境,再后来进入KDE安装或者卸载软件的时候,时不时的出现这样的错误提示:

Unknown media type in type 'all/all'
Unknown media type in type 'all/allfiles'
Unknown media type in type 'uri/mms'
Unknown media type in type 'uri/mmst'
Unknown media type in type 'uri/mmsu'
Unknown media type in type 'uri/pnm'
Unknown media type in type 'uri/rtspt'
Unknown media type in type 'uri/rtspu'
Unknown media type in type 'interface/x-winamp-skin'

Google一下发现,这个BUG早就有人提交过了。解决办法在这里
首先找到kde.xml,我的路径在

/usr/share/mime/packages/kde.xml

先备份

sudo cp /usr/share/mime/packages/kde.xml /usr/share/mime/packages/kde.xml.backup

然后编辑一下,找到相关的标签,用”all/all”关键字一下就能找到,删除之。

2011-8-7 更新

猜想这是KDE桌面搜索用的,我开机时经常遇到virtuoso数据库未安装的错误提示,这是一个高效的面向对象的SQL数据库。重新安装该数据库后,即使恢复“all/all”等metadata信息也不会再看到错误提示。

流程是这样的,Strigi负责收集信息,制作成一个RDF信息库,用Virtuoso存储,Soprano是一个处理RDF的Library。

这整个一套系统就是传说中的Nepomuk项目,他是KDE桌面搜索的真正驱动力。

 

参考:

Strigi : 一个桌面搜索Daemon http://en.wikipedia.org/wiki/Strigi

NEMPOMUK : The Social Semantic Search http://nepomuk.semanticdesktop.org/xwiki/bin/view/Main1/

Virtuoso : a high-performance object-relational SQL database http://en.wikipedia.org/wiki/Virtuoso_Universal_Server

Posted in Linux | Tagged | Leave a comment