由于编写Marvel 88w8686 wlan模块SPI接口的stm32驱动的需要,对Marvel 88w8686 Linux SPI接口的驱动进行了分析和参考。
#Marvel驱动的使用#
88w8686的Linux驱动是通过模块方式载入内核的,在使用时加载进去就行了,88w8686的Linux驱动还包含了sdio的驱动,加载时,还必须加载sdio驱动。在运行make
命令进行编译后,会生成sdio.o
和sd8386.o
文件,在终端下输入下列命令进行加载:
1 |
|
#Marvel驱动源码分析#
了解完如何使用Marvel 88w8686 Linux驱动后,便可以进行驱动源码的分析。源码点击此处下载。
##源码目录结构##
源码的目录结构如下:
1 |
|
其中app文件夹是驱动自带的wlanconfig工具,用于配置wifi的参数,本文不进行分析。config文件夹包含的是一些配置文件,不进行分析。if文件夹包含的是关于GSPI操作的接口,包括固件。io文件夹包含的是GSPI驱动。os文件夹包含的是与系统相关的操作与头文件。wlan文件夹是wifi驱动的主要目录,在本文进行重点分析。
##驱动的启动流程##
由上文可知,wlan驱动是通过insmod
进加载的。在文件wlan/wlan_main.c
末尾处可以看到:
1 |
|
这几个宏说明了wlan驱动模块的入口,知道其初始化函数是wlan_init_module
。
再来看看wlan_init_module
函数,在其函数中,调用了以下函数对网卡插入和移除时调用的函数进行了注册,即当电脑检测到网卡时有调用wlan_add_card
。
1 |
|
在wlan_add_card
中,进行初始化后,以下函数启动了内核主线程wlan_service_main_thread
。
1 |
|
###内核主线程###
内核主线程wlan_service_main_thread
承担了wlan驱动的主要工作,它主要是处理wlan固件的事件,wlan固件接受到的数据和内核传来的数据。
在wlan_service_main_thread
函数中有一个for
死循环,所有数据处理都在循环里面。
在主循环里面,首先调用:
1 |
|
让主线程进入可中断的等待模式,等到事件到来。当主线程被唤醒时,即有数据要处理,便调用:
1 |
|
先读取网卡固件发来的数据及标志位,将标志位存入Adapter->HisRegCpy
。
紧接着便对标志位进行判断:
1 |
|
并且调用相应的处理函数,对数据进行处理。
当然进程中还对wlan的PS(Power Save)模式和WMM(WiFi-MultiMedia)进行判断处理,本文不进行分析。
接着,便对内核发来的命令进行处理(比如说连接命令,扫描命令):
1 |
|
当然,之后便进入等待事件状态,重复以上过程。
###wlan固件数据/事件###
当wlan固件有数据/事件时,GSPI驱动会产生中断,它的中断处理函数为sbi_interrupt
,在sbi_interrupt
中调用了wlan_interrupt
,在wlan_interrupt
中,调用
1 |
|
唤醒了主线程,对数据进行处理。上面介绍过,主线程调用sbi_get_int_status(priv, &ireg)
读取数据和标志位。
###应用层调用驱动接口###
该wlan驱动接口是wext(Wireless Extensions无线扩展接口)。wext的接口实现上,应用层采用ioctl方式访问驱动,设置无线参数或者获取无线参数,配置无线驱动进行联网操作。无线驱动事件到应用层的传递采用的netlink socket技术,一种netlink route消息技术。这也是很多其他类型的驱动标准的实现方法。具体调用方法可以参考wlanconfig
,比如说
1 |
|
调用wlan驱动扫描命令。如下函数
1 |
|
新建了一个netlink连接。
所有的ioctl的回调函数在,wlan_wext.c的wlan_handler
数组中,这里对SIOCGIWSCAN
即wlan扫描进行分析。可以看到wlan扫描的回调函数是wlan_get_scan
和wlan_set_scan
。
先来看看wlan_set_scan
,在wlan_set_scan
函数中又调用了wlan_scan_networks
,在wlan_scan_networks
中调用了wlan_scan_channel_list
将命令添加到命令队列中,并等待命令执行响应,然后调用wlan_scan_process_results
更新priv
中的扫描表。
在函数wlan_scan_channel_list
中调用PrepareAndSendCommand
将命令添加到命令列表。等发送到固件的数据和固件发送过来的数据都存在CmdNode->BufVirtualAddr
指针指向的数据中,接着调用
1 |
|
将命令加入命令队列,接着唤醒主线程处理命令,然后执行如下函数
1 |
|
等待结果,当CmdNode->CmdWaitQWoken
为TRUE
时唤醒。
在主线程中,由上面知道,调用ExecuteNextCommand
执行内核发来的命令。在ExecuteNextCommand
函数中调用DownloadCommandToStation
下载命令,DownloadCommandToStation
中,先调用sbi_host_to_card
下载命令,然后调用ModTimer
进行超时时重新发送。
接下来,便是等待响应,当固件对发来的命令响应时,会触发中断,如上文说的调用wlan_process_rx_command
处理固件对命令的响应。在wlan_process_rx_command
中处理完成之后,会调用CleanupAndInsertCmd
回收命令,此时的CmdNode->CmdWaitQWoken
为TRUE
,下次schedule
时便,唤醒上面等待命令响应的线程,让其继续执行。
整个驱动运行的流程便是这样子。