S5pc100平台wifi模块驱动移植分析
一、移植环境:
1、 主机:Ubuntu 10.10发行版
2、 目标机:FS_S5PC100平台
3、 交叉编译工具:arm-none-linux-gnueabi-
4、wifi模块:marvell-8686
5、内核:linux-2.6.35
---------------------------------------------------------------------
二、在linux下的移植
1. 平台代码修改
内核驱动支持
因为S5PC100平台上SDIO功能已经支持,所以只需要内核驱动支持marvell-8686即可。
由于wifi的接口种类有很多种,比如有usb接口wifi,串口接口wifi,sd卡接口wifi,sdio接口wifi,所以在移植wifi驱动时必须确保usb,串口,sd卡的驱动存在,取决于wifi接口的类型。S5pc100开发板上的wifi的接口是sdio接口的,所以在此之前必须移植号sdio接口的驱动。
$ make menuconfig 修改:
[*] Networking support --->
[*] Wireless --->
<*> cfg80211 - wireless configuration API
{*} common routines for IEEE 802.11 drivers
Device Drivers --->
[*] Network device support --->
Wireless LAN --->
<*>Marvell 8xxx Libertas WLAN driver support
<*> Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards
Generic Driver Options --->
(sd8686.bin sd8686_helper.bin) External firmware blobs to build into the kernel
2. 内核的修改:
wifi的移植依赖于网卡驱动的移植和nand-flash平台的支持,可以参考Linux-2.6.35内核移植—网卡驱动的移植和Linux-2.6.35内核移植——Nand flash 驱动的移植文件arch/arm/mach-s5pc100/mach-smdkc100.c下有内核的分区, 把第2个分区改成8M。因为生成的文件系统太大,大于4M。修改如下(红色字体部分):
static struct mtd_partition s5pc100_nand_part[] = {
[0] = {
.name = "bootloader",
.size = SZ_1M,
.offset = 0,
},
[1] = {
.name = "kernel",
.size = SZ_1M * 4,
.offset = MTDPART_OFS_APPEND,
},
[2] = {
.name = "rootfs",
.size = SZ_8M,
.offset = MTDPART_OFS_APPEND,
},
[3] = {
.name = "usrfs",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
},
3. 固件的准备
将固件sd8686.bin和sd8686_helper.bin拷贝到linux-2.6.35/firmware下。编译时会自动编译进内核。
$ cp sd8686.bin linux-2.6.35/firmware/$ cp sd8686_helper.bin linux-2.6.35/firmware/$ make zImage
$ sudo cp zImage /tftpboot
由于wifi驱动在运行时需要两个工具,wpa_supplicant和wpa_cli,所以必须先制作这两个工具放到文件系统的bin目录下面。
三,文件系统的制作
1. wpa_suppicant的移植
源码版本选择:
wpa_supplicant-0.7.3
openssl-0.9.8e
[ openssl移植 ]:
a、补丁安装
openssl源码编译需要打wpa_supplicant提供的补丁,我们选择的openssl版本为openssl-0.9.8e所以我们需要wpa_supplicant-0.7.3/patches/openssl-0.9.8e-tls-extensions.patch拷贝到openssl源码下。
$ cp wpa_supplicant-0.7.3/patches/openssl-0.9.8e-tls-extensions.patch openssl-0.9.8e/$ cd openssl-0.9.8e
$ patch -p1 < openssl-0.9.8e-tls-extensions.patch注意:-p1,这里是数字“1”.
b、配置编译安装
$ ./Configure linux-elf-arm -DL_ENDIAN linux:'arm-cortex_a8-linux-gnueabi-gcc' shared --prefix=/home/linux/workdir/wifi/openssl 打开openssl-0.9.8e中Makefile
$ vim Makefile 修改如下:
- CC= cc
+ CC= arm-none-linux-gnueabi-
- AR= ar $(ARFLAGS) r
+ AR= arm-cortex_a8-linux-gnueabi-ar $(ARFLAGS) r
- RANLIB= /usr/bin/ranlib
+ RANLIB= arm-cortex_a8-linux-gnueabi-ranlib编译:
$ make
$ make install[ wpa_supplicant移植 ]:
在/wpa_supplicant-0.7.3/wpa_supplicant目录下
$ cd wpa_supplicant-0.7.3/wpa_supplicant
a、Makefile修改
$ vim Makefile
修改#ifndef CC
CC=gcc
#endif为CC= arm-none-linux-gnueabi--gcc //自己的交叉编译工具链在
CFLAGS += -I../src
CFLAGS += -I../src/utils
下添加:
CFLAGS += -I/home/linux/workdir/wifi/openssl/include//注意这是你自己安装wpa_supplicant的路径下的include
修改
LIBS += -lssl
为LIBS += -lssl -L/home/linux/workdir/wifi/openssl/lib/
修改
LIBS_p += -lcrypto
为
LIBS_p += -lcrypto -L/home/linux/workdir/wifi/openssl/lib/
b、编译
$ cp deconfig .config
$ make此处可能出题以下问题:
/usr/bin/ld: skipping incompatible /usr/local/ssl/lib/libssl.so when searching for -lssl
/usr/bin/ld: skipping incompatible /usr/local/ssl/lib/libssl.a when searching for -lssl
/usr/bin/ld: cannot find -lssl
/usr/bin/ld: skipping incompatible /usr/local/ssl/lib/libcrypto.so when searching for -lcrypto
/usr/bin/ld: skipping incompatible /usr/local/ssl/lib/libcrypto.a when searching for -lcrypto
/usr/bin/ld: cannot find -lcrypto
collect2: ld returned 1 exit status
make: *** [wpa_supplicant] Error 1
解决方法如下:
方法1:可能你在修改wpa_supplicant-0.7.3/wpa_supplicant下Makefile的时候,将CC=gcc直接改为CC=arm-cortex_a8-linux-gnueabi-gcc,而没有按上面方式去掉#ifndef CC和#endif这两行。
方法2:需要先编译安装openssl库,确保两个编译都使用统一工具arm-cortex_a8-linux-gnueabi-gcc,这样只要把openssl和wpa_supplicant两个用同一个编译工具编译即可。
方法3:如果已经安装了openssl库,还出现如上问题,建议更新源,卸载后再装,如果有文件信赖关系不对,运行命令:sudo apt-get -f install.
注:方法1,亲测可行。方法2和3是我在移植的时候出现上面问题,在网上搜到的结果,好像不管用,在此帖出,以供参考。
最后,编译
$ make后将生产的wpa_supplicant和wpa_cli拷贝到根文件系统的bin目录下
$ cp wpa_supplicant /source/rootfs/bin
$ cp wpa_cli /source/rootfs/bin2. 文件系统的制作
至此,wifi的驱动工作已经完成。但是想正常的利用wifi来链接网络,还需要配置wifi。
Wifi配置:
$ cd 文件系统
a. 在根文件中添加/etc/resolv.conf,域名解析。
$ vim /etc/resolv.conf内容为:
# Generated by NetworkManager
nameserver 192.168.1.1 //你自己的无线路由的DNS
nameserver 8.8.8.8 //此行可不写
b. 在根文件系统/etc下添加wpa_supplicant配置文件wpa_supplicant.conf
$ vim /etc/wpa_supplicant.conf内容为:
# WPA-PSK/TKIP
ctrl_interface=/var/run/wpa_supplicant
network={
ssid="XXXX" #无线路由ssid
key_mgmt=WPA-PSK
proto=WPA
pairwise=TKIP
group=TKIP
psk="XXXXXXXX" #无线路由密码
}c. 创建目录/var/run/wpa_supplicant
$ mkdir /var/run/wpa_supplicant –p
四,wifi模块的测试
启动开发板完成如下操作:
● 配置无线网卡ip
# ifconfig wlan0 192.168.1.200 # ifconfig -a
● 配置默认网关
# route add default gw 192.168.1.1 ● 启动wpa_supplicant连接无线网络
# wpa_supplicant -B -iwlan0 -c /etc/wpa_supplicant.conf问题:wifi libertas: problem fetching packet from firmware
解决:网上有好多解决方法,但都不适用我的情况,最后换了一个wifi模块,就好了。大家也可以看看是不是模块的问题。
● 查看连接状态
# wpa_cli status
路由器在设置的时候,也要按照上图中对应的参数进行设置,不然下面会ping不通的。
ping www.linuxidc.com
完成 !
第二篇:wifi驱动分析
Atheros wifi 驱动分析
作者:jingwenyi
时间:2014/7
Ar6003 驱动文档摘要
1、 wmi : wireless module interface //无线模块结构
2、 bmi : bootloader message interface
3、 htc : host target communications
4、 wps:wifi protected setup
5、 CS:connection services module
6、 STA:station
7、 AP:access point
Wireless application : 生产数据和消费数据
Wireless module interface (WMI):host 和target 之间的通信协议
Host/target communications (HTC): 发送和接收数据
Hardware interface (HIF) :调用硬件接口发送和接收数据(这里用的是sdio 接口)
Bootloader message interface (BMI):在wifi芯片启动时通信协议,可以下载bin文件到wifi芯片中。
Ar6000 wifi 驱动分析(AP 模式分析)
代码执行的主要流程
//挂载sdio 驱动到内核和注册网络设备
module_init(__ar6000_init_module);
?__ar6000_init_module
?status = ar6000_init_module();
?status = HIFInit(&osdrvCallbacks);
? status = sdio_register_driver(&ar6k_driver);注册sdio 驱动(这里直接调用的内核sdio协议栈)
?.probe = hifDeviceInserted, //执行驱动的probe 函数
?ret = hifEnableFunc(device, func);
? kthread_create(async_task, //内核开了一个sdio 异步发送数据的进程
? taskFunc = startup_task; //开一个内核进程,执行startup_task 进程
?if ((osdrvCallbacks.deviceInsertedHandler(osdrvCallbacks.context,device)) != A_OK)//调用安装wifi 设备的函数指针ar6000_android_avail_ev,这个函数是在android_module_init中注册的。 ?ar6000_android_avail_ev
?ret = ar6000_avail_ev_p(context, hif_handle);
?ar6000_avail_ev//这个函数指针赋值赋的很曲折首先在ar6000_init_module(void)函数中赋给了osdrvCallbacks.deviceInsertedHandler = ar6000_avail_ev;然后在android_module_init(&osdrvCallbacks);中ar6000_avail_ev_p = osdrvCallbacks->deviceInsertedHandler;赋给了ar6000_avail_ev_p;
? BMIInit();//开始启动wifi模块
?ar->arHtcTarget = HTCCreate(ar->arHifDevice,&htcInfo);//创建htc ,关闭中断
? status = (ar6000_init(dev)==0) ? A_OK : A_ERROR; //
初始化网络设备
? (BMIDone(ar->arHifDevice) != A_OK))//bmi 启动完成
? status = HTCStart(ar->arHtcTarget);//启动htc ,开启中断
?status = DevUnmaskInterrupts(&target->Device);//开启中断, 注册中断处理函数
?HIFUnMaskInterrupt(pDev->HIFDevice);//注册中断处理函数
?ret = sdio_claim_irq(device->func, hifIRQHandler);//注册中断处理函数,中断后就会调用hifIRQHandler 这个处理函数
? if (register_netdev(dev)) //向内核注册网络设备,到此初始化完成。
//产生中断后的代码流程
?hifIRQHandler(struct sdio_func *func) //中断处理函数 (hif.c)
?status = device->htcCallbacks.dsrHandler(device->htcCallbacks.context); //设备处理函数的函数指针
?A_STATUS DevDsrHandler(void *context);这函数是在创建htc 即HTCCreate 函数中填充的:HTCCreate—>DevSetup?htcCallbacks.dsrHandler = DevDsrHandler;(ar6k_events.c)
?status = ProcessPendingIRQs(pDev, &done, &asyncProc);//处理未决事件的函数,在这里会循环处理(ar6k_events.c)
?status = pDev->MessagePendingCallback();这个函数指针也是在HTCCreate 中填充的target->Device.MessagePendingCallback = HTCRecvMessagePendingHandler;(htc_recv.c) ? HTCRecvMessagePendingHandler();
? DO_RCV_COMPLETION(pEndpoint,&container);
? DoRecvCompletion();
? pEndpoint->EpCallBacks.EpRecv(pEndpoint->EpCallBacks.pContext, pPacket);//这个函数指针是在ar6000_init 中填充的connect.EpCallbacks.EpRecv = ar6000_rx; ar6000_rx 是一个非常重要的函数。 ? ar6000_rx(void *Context, HTC_PACKET *pPacket)
//数据发送流程
? ar6000_data_tx(struct sk_buff *skb, struct net_device *dev)
? HTCSendPkt(ar->arHtcTarget, &cookie->HtcPkt);
? return HTCSendPktsMultiple(HTCHandle, &queue);
? HTCTrySend(target, pEndpoint, pPktQueue);
? HTCIssueSend(target, pPacket);
? status = DevSendPacket(&target->Device,
? status = HIFReadWrite(pDev->HIFDevice, //传给了sido 总线
? AddToAsyncList(device, busrequest);//把要发送的数据包加入异步发送队列
? up(&device->sem_async); //获取信号量,用内核进程进行数据发送
? static int async_task(void *param) //发送数据
? __HIFReadWrite();//发送数据
? sdio_writesb();sdio_memcpy_toio();sdio_readsb();sdio_memcpy_fromio();
? down_interruptible(&busrequest->sem_req) != 0 //释放信号量
//中断发送或,接收流程
?HTCRecvMessagePendingHandler
? status = HTCIssueRecv(target, pPacket);//异步接收数据包
? status = HIFReadWrite(pDev->HIFDevice, //命令传给sdio 总线 ?与发送流程相同
//sta 连接流程
? ar6000_rx ();收到连接的命令,此时的ar->arControlEp=ept=1
? wmi_control_rx(arPriv->arWmi, skb);//解析命令
? case (WMI_CONNECT_EVENTID): //连接命令
? status = wmi_connect_event_rx(wmip, datap, len);
? A_WMI_CONNECT_EVENT(wmip->wmi_devt, ev);
? ar6000_connect_event((devt), (pEvt));
? wireless_send_event(arPriv->arNetDev, IWEVREGISTERED, &wrqu, NULL); //向网络层发送事件 wext-core.c
?skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); //wext-core.c
----------------------------------经过网络层的处理-------------------------------------------------------------------- ?sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)// net/socket.c
?err = dev_ioctl(net, cmd, argp); // net/core/dev.c
?return wext_handle_ioctl(net, &ifr, cmd, arg); // net/wireless/wext-core.c
?ret = wext_ioctl_dispatch(net, ifr, cmd, &info,
ioctl_standard_call,
ioctl_private_call); // net/wireless/wext-core.c
?ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private); //net/wireless/wext-core.c ? return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd);//这个是在注册网络设备注册的函数.ndo_do_ioctl = ar6000_ioctl,
---------------------------网络层调用驱动层的函数----------------------------------------
? int ar6000_ioctl(); // ioctl.c
? case IEEE80211_IOCTL_SETKEY: //ioctl.c
? ar6000_ioctl_setkey(arPriv, &keydata); //ioctl.c
? status = ar6000_sendkey(arPriv, ik, keyUsage); //ioctl.c
? status = wmi_addKey_cmd()
? status = wmi_cmd_send(wmip, osbuf, WMI_ADD_CIPHER_KEY_CMDID, sync_flag);
//数据传输流程
? ar6000_rx ();收到连接的数据,此时ept=2
? ar6000_deliver_frames_to_nw_stack((void *) arPriv->arNetDev, (void *)skb);
? A_NETIF_RX_NI(skb);
? netif_rx_ni(skb) //将数据传给ip 层
Hostapd 设置ssid
?int main(int argc, char *argv[]); //main.c
?interfaces.iface[i] = hostapd_interface_init(&interfaces, //main.c
?hostapd_setup_interface(iface)) { //main.c
?ret = setup_interface(iface); //hostapd.c
?return hostapd_setup_interface_complete(iface, 0); //hostapd.c
?if (hostapd_driver_commit(hapd) < 0) { // ap_drv_ops.c
?return hapd->driver->commit(hapd->drv_priv);//在driver_ar6000.c 中赋值的.commit = ar6000_commit
?ar6000_commit(void *priv)//driver_ar6000.c
----------------------从应用层通过ioctl 进入驱动层---------------------------------------------------------------
?if (ioctl(drv->ioctl_sock, SIOCSIWCOMMIT, &iwr) < 0) { //在wireless_ext.c 中赋值的
(iw_handler) ar6000_ioctl_siwcommit,
?ar6000_ioctl_siwcommit(struct net_device *dev, // wireless_ext.c
?ar6000_ap_mode_profile_commit(arPriv); //ar6000_dr.c
?wmi_ap_profile_commit(arPriv->arWmi, &p); //wmi.c
?status = wmi_cmd_send(wmip, osbuf, WMI_AP_CONFIG_COMMIT_CMDID,
NO_SYNC_WMIFLAG); //wmi.c
下面我把我分析的ar6003驱动架构图粘出来