和菜鳥一起學linux之wifi學習記錄

http://blog.csdn.net/eastmoon502136/article/details/8496258


 也差不多一個月沒有更新博客了,元旦也過去了,8天的班也上完了,小病也好了,時間又回到了每個周的週末了,不知道幹嘛,突然發現好像失去了什麼,好像做了很多很多沒有意義的事,一直都是在原點打轉,不知道接下去會是什麼,不知道爲了什麼。也許總會有一段日子覺得很迷茫,不知所措。這麼一來,原本要做的很多事都耽擱了,2012,離夢還有那麼遠,沒有絲毫的前進。很累,但是不知道是身體的。還是心裏的,還是…………

       不說這些了,還是把上次總結的wifi驅動給分析分析吧。

       Sdio wifi,首先,他是一個sdio的卡的設備,然後具備了wifi的功能,所以,註冊的時候還是先以sdio的卡的設備去註冊的。然後檢測到卡之後就要驅動他的wifi功能了,顯然,他是用sdio的協議,通過發命令和數據來控制的。雖然wifi數據的傳輸流程,但是也得知道他是怎麼工作的,要不然連函數指針在哪都找不到,那就更別說其他的了。好了,還是先從初始化開始吧。

----------------------------------------------------華麗的分割線------------------------------------------------

 

關於sdio初始化

----------------------------------------------------------------------------------------------------------------------

 

首先是模塊初始化os_dep/linux/sdio_intfs.c函數中調用下面這個函數。

static int __init rtw_drv_entry(void)

這裏做了一些初始化工作,主要是:

ret = sdio_register_driver(&sdio_drvpriv.r871xs_drv);//註冊了sdio的驅動。

sdio_drvpriv = {

       .r871xs_drv.probe = rtw_drv_init,

       .r871xs_drv.remove = rtw_dev_remove,

       .r871xs_drv.name = (char*)DRV_NAME,

       .r871xs_drv.id_table = sdio_ids,

       .r871xs_drv.drv = {

              .pm = &rtw_sdio_pm_ops,

       }

};

所以當檢測到sdio卡插入了之後就會調用rtw_drv_int,而當卡被移除後就會調用rtw_dev_remove。還有電源管理啊什麼的,就不細看了。下面主要還是看下rtw_drv_int函數吧。

if ((dvobj =sdio_dvobj_init(func)) == NULL)

if ((if1 =rtw_sdio_if1_init(dvobj, id)) == NULL)

if ((if2 =rtw_drv_if2_init(if1, NULL, sdio_set_intf_ops)) == NULL)

這裏主要是sdio的host和wifi的綁定,可以讓wifi用這個host來傳輸數據用的。看下這個函數就知道了sdio_set_intf_ops。這個函數裏,主要是sdio的數據傳輸接口的一些函數指針的賦值。

pops->_read8= &sdio_read8;

       pops->_read16= &sdio_read16;

       pops->_read32= &sdio_read32;

       pops->_read_mem= &sdio_read_mem;

       pops->_read_port= &sdio_read_port;

 

       pops->_write8= &sdio_write8;

       pops->_write16= &sdio_write16;

       pops->_write32= &sdio_write32;

       pops->_writeN= &sdio_writeN;

       pops->_write_mem= &sdio_write_mem;

       pops->_write_port= &sdio_write_port;

最後wifi上面傳輸的數據,會調用這裏的接口來通信的。

 

這裏還分配了irq在接收數據的時候要用到

alloc_irq(dvobj) != _SUCCESS)

其調用了err = sdio_claim_irq(func,&sd_sync_int_hdl);

最後就是那個sd_sync_int_hdl函數的功能了

----------------------------------------------------華麗的分割線------------------------------------------------


關於Wifi初始化

----------------------------------------------------------------------------------------------------------------------

這個函數在os_dep/linux/os_intfs.c中的rtw_drv_if2_init進行初始化工作。

1、初始化netdev

       pnetdev = rtw_init_netdev(NULL);

       這個函數主要是:

a、pnetdev->netdev_ops = &rtw_netdev_ops;//函數指針的結構體的賦值

rtw_netdev_ops = {

       .ndo_open = netdev_open,

       .ndo_stop = netdev_close,

       .ndo_start_xmit = rtw_xmit_entry,

       .ndo_select_queue  = rtw_select_queue,

};

b、pnetdev->wireless_handlers = (struct iw_handler_def*)&rtw_handlers_def;//無線的handler

       c、loadparam(padapter, pnetdev);//一些參數的配置,wlan0這個接口名字也是這定義的。

 

2、netdev函數指針的結構體的賦值

pnetdev->netdev_ops = &rtw_netdev_if2_ops;

rtw_netdev_if2_ops = {

       .ndo_open = netdev_if2_open,

    .ndo_stop = netdev_if2_close,

    .ndo_start_xmit = rtw_xmit_entry,

    .ndo_set_mac_address =rtw_net_set_mac_address,

    .ndo_get_stats = rtw_net_get_stats,

    .ndo_do_ioctl =rtw_ioctl,

       .ndo_select_queue  = rtw_select_queue,

};

在上面已經初始化了,這裏難道是添加了rtw_net_set_mac_address,rtw_ioctl嗎?好吧,就當作上面無效,只用下面的就OK了。

 

3、初始化adapter

 

4、設置Hal的function, 分配Hal的數據

hal_set_hal_ops(padapter);

#define hal_set_hal_ops rtl8723as_set_hal_ops

所以調用的是hal/rtl8723a/sdio/sdio_halinit.c中的rtl8723as_set_hal_ops

這裏又調用了hal/rtl8723a/rtl8723a_hal_init.中的rtl8723a_set_hal_ops(pHalFunc);而這個函數又設置了函數指針,其中有下面的函數指針的賦值:

pHalFunc->run_thread= &rtl8723a_start_thread;

 

回到主函數,該函數主要是設置一些函數指針,其中有下面幾個函數指針的賦值:

pHalFunc->init_xmit_priv =&rtl8723as_init_xmit_priv;

pHalFunc->init_recv_priv =&rtl8723as_init_recv_priv;

pHalFunc->hal_init = &rtl8723as_hal_init;

       pHalFunc->hal_xmit= &rtl8723as_hal_xmit;

pHalFunc->mgnt_xmit = &rtl8723as_mgnt_xmit;

 

5、rtw_hal_read_chip_version(padapter);//讀取芯片版本

if(rtw_init_drv_sw(padapter)!=_SUCCESS) //這裏可是做了不少東西啊,初始化了很多的driver的數據。主要做了以下這些事情:

if ((rtw_init_cmd_priv(&padapter->cmdpriv)) == _FAIL)

if ((rtw_init_evt_priv(&padapter->evtpriv)) == _FAIL)

if (rtw_init_mlme_priv(padapter) == _FAIL)//

if(init_mlme_ext_priv(padapter) == _FAIL)

if(rtw_init_tdls_info(padapter) == _FAIL)

if(_rtw_init_xmit_priv(&padapter->xmitpriv,padapter) == _FAIL)//發送數據的隊列,frame

的buf大小等的設置

if(_rtw_init_recv_priv(&padapter->recvpriv,padapter) == _FAIL)//接收數據的隊列。Frame

的buf大小等的設置

rtw_init_netdev_name(pnetdev,name);//初始化網絡接口名

 

6、_rtw_memcpy(mac,primary_padapter->eeprompriv.mac_addr, ETH_ALEN);

//從eeprom中讀取mac地址

 

7、if(register_netdev(pnetdev) != 0)   //這裏去註冊netdev

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------華麗的分割線------------------------------------------------

 

 

關於Wifi工作總流程

----------------------------------------------------------------------------------------------------------------------

 

----------------------------------------------------華麗的分割線------------------------------------------------

關於Wifi接口激活

----------------------------------------------------------------------------------------------------------------------

       相信都知道網絡調用到底層就是通過ioctl的接口,而應用層是socket這個網絡套接字來調用的。底層主要實現的接口就如下結構體所示了。

rtw_netdev_ops = {

       .ndo_open = netdev_open,

       .ndo_stop = netdev_close,

       .ndo_start_xmit =rtw_xmit_entry,

       .ndo_select_queue  = rtw_select_queue,

};

       是不是一目瞭然?對的,和那個char設備很類似額。

       首先,我們一般都會在linux下用一條命令,那便是:ifconfig wlan0 up。這裏的wlan0是初始化的時候弄好的,而對於我們本地的網卡,一般也是有一個eth0的接口的。所以,這個時候就調用了我們的netdev_open接口了。沒錯。那就先看看這裏幹了什麼事情了。

os_dep/linux/os_intfs.c中的   ret = _netdev_open(pnetdev);

調用了這個函數,還用鎖給鎖住的。這裏纔是主要的處理了。

1、硬件抽象層的初始化

status =rtw_hal_init(padapter);

       status=        padapter->HalFunc.hal_init(padapter->pbuddy_adapter);

而HalFunc.hal_init這個函數指針在初始化的時候已經賦值給了

hal/rtl8723a/sdio/sdio_halinit.c文件中的rtl8723as_hal_init這個函數,這個函數的調用了很多東東了。

       ret= _InitPowerOn(padapter);    //開電源

       ret= InitLLTTable(padapter, boundary);

       ret= rtl8723a_FirmwareDownload(padapter);//下載firmware

rtl8723a_InitializeFirmwareVars(padapter);

HalDetectPwrDownMode(padapter);//低功耗模式

_InitRFType(padapter);   // Set RF type for BB/RF configuration

ret =PHY_MACConfig8723A(padapter); //物理層的一些操作

ret =PHY_BBConfig8723A(padapter);

…………這裏做了很多的物理層的操作就不細看了。

 

2、開兩個線程,用來發送命令和數據。

status=rtw_start_drv_threads(padapter);

a、這裏先開一個cmd線程

padapter->cmdThread=kernel_thread(rtw_cmd_thread,padapter,CLONE_FS|CLONE_FILES);

cmd線程處理函數rtw_cmd_thread在core/rtw_cmd.c中,他調用了

這裏主要是:

cmd_hdl = wlancmds[pcmd->cmdcode].h2cfuns;

pcmd_callback =rtw_cmd_callback[pcmd->cmdcode].callback;


上層通過ioctl調用下來後,會根據具體的handle和callback函數調用對應的函數做相應的處理的。

b、再開一個數據線程

rtw_hal_start_thread(padapter);然後調用這個函數指針。

padapter->HalFunc.run_thread(padapter);

其在初始化的時候被賦值爲rtl8723a_start_thread

pHalData->SdioXmitThread = kernel_thread(rtl8723as_xmit_thread, padapter, CLONE_FS|CLONE_FILES);//又開了一個rtl8723as_xmit_thread線程了。傳輸數據的。

具體在hal/rtl8723a/sdio/rtl_8723as_xmit.c

調用了ret =rtl8723as_xmit_handler(padapter);

接着是err = rtw_hal_xmit_thread_handler(padapter);

padapter->HalFunc.xmit_thread_handler(padapter);函數指針調用過程,

              會調用rtl8723as_xmit_buf_handler

然後是queue_empty =rtl8723_dequeue_writeport(padapter, freePage);

最後調用rtw_write_port(padapter,deviceId, pxmitbuf->len, (u8 *)pxmitbuf);和sdio控制器交互。


       3、初始化wifi的mac子系統管理實體

if (init_hw_mlme_ext(padapter) == _FAIL)

 

4、初始化cfg80211wifi的phy

rtw_cfg80211_init_wiphy(padapter);

      

       5、開啓或者喚醒隊列。

              if(!rtw_netif_queue_stopped(pnetdev))

                     rtw_netif_start_queue(pnetdev);

              else

                     rtw_netif_wake_queue(pnetdev);

      

       ok了,基本open了之後,就做了這些工作。

------------------------------------------------華麗的分割線------------------------------------------------

 

 關於wifi的數據傳輸

----------------------------------------------------------------------------------------------------------------------

1、接收數據

os_dep/linux/sdio_intfs.c中的中斷函數sd_sync_int_hdl這個可以知道,每次數據接收就是通過這個中斷函數來的。

其調用了hal/rtl8723a/sdio/sdio_ops.c的sd_int_hdl(psdpriv->if1); 接着sd_int_dpc(padapter);

若fifo不空,那麼就調用

precvbuf = sd_recv_rxfifo(padapter,phal->SdioRxFIFOSize);其主要做了

a、首先是分配recvbuf

      b、再分配的是skb

c、然後從rxfifo中讀數據

d、最後是初始化recvbuf

如果上面成功了,那麼就調用下面的函數了

sd_rxhandler(padapter, precvbuf);

首先那便是enqueue recvbuf

rtw_enqueue_recvbuf(precvbuf, ppending_queue);

接着會調度tasklet去做處理的

tasklet_schedule(&precvpriv->recv_tasklet);

又因爲pHalFunc->init_recv_priv= &rtl8723as_init_recv_priv;其中init就有tasklet函數的初始化等工作。所以即調用了hal/rtl8723as/sdio/rtl8723as_recv.c 中的

rtl8723as_recv_tasklet函數了

precvbuf =rtw_dequeue_recvbuf(&precvpriv->recv_buf_pending_queue);//先出隊列

update_recvframe_attrib(precvframe, (structrecv_stat*)ptr);//幀解析

pkt_copy = netdev_alloc_skb(padapter->pnetdev,alloc_sz);//封裝skb,使得上層可以處理它。然後就是寫入數據,傳給上層了。

OK,數據從sdio接口讀取,然後交給上層就是這樣了。


2、數據發送

已經在wifi初始化的時候.ndo_start_xmit= rtw_xmit_entry,了,所以發送函數就調用到了

rtw_xmit_entry函數會調用到res = rtw_xmit(padapter,&pkt);

do_queue_select(padapter, &pxmitframe->attrib)//他會選擇一個隊列,因爲有4個隊列啊。

之後就會通過if (rtw_hal_xmit(padapter, pxmitframe) == _FALSE)

然後padapter->HalFunc.hal_xmit(padapter,pxmitframe);這個調用到了抽象層的sdio去了。

由上面的初始化可以知道pHalFunc->hal_xmit =&rtl8723as_hal_xmit;

其在hal/rtl8723as/sdio/rtl8723as_xmit.c中。

他會調用err = rtw_xmitframe_enqueue(padapter, pxmitframe);,接着是

rtw_xmit_classifier(_adapter*padapter, struct xmit_frame *pxmitframe);

然後這裏他會選擇一個隊列去放數據。

----------------------------------------------------華麗的分割線----------------------------------------------


關於wifi的應用調用接口

----------------------------------------------------------------------------------------------------------------------

最主要的就是rtw_handlers[]了。

rtw_wx_get_name,        /* SIOCGIWNAME */

rtw_wx_set_freq,          /* SIOCSIWFREQ */

rtw_wx_get_freq,         /* SIOCGIWFREQ */

rtw_wx_set_mode,        /* SIOCSIWMODE */

rtw_wx_get_mode,              /* SIOCGIWMODE */

rtw_wx_get_sens,         /* SIOCGIWSENS */

rtw_wx_get_range,              /* SIOCGIWRANGE */

rtw_wx_set_priv,          /* SIOCSIWPRIV */

rtw_wx_set_wap,         /* SIOCSIWAP */

rtw_wx_get_wap,         /* SIOCGIWAP */

rtw_wx_set_mlme,        /* request MLME operation; uses struct iw_mlme */

rtw_wx_set_scan,         /* SIOCSIWSCAN */

rtw_wx_get_scan,         /* SIOCGIWSCAN */

rtw_wx_set_essid,        /* SIOCSIWESSID */

rtw_wx_get_essid,        /* SIOCGIWESSID */

rtw_wx_get_nick,         /* SIOCGIWNICKN */

rtw_wx_set_rate,          /* SIOCSIWRATE */

rtw_wx_get_rate,          /* SIOCGIWRATE */

rtw_wx_set_rts,                   /* SIOCSIWRTS */

rtw_wx_get_rts,                  /* SIOCGIWRTS */

rtw_wx_set_frag,          /* SIOCSIWFRAG */

rtw_wx_get_frag,         /* SIOCGIWFRAG */

rtw_wx_get_retry,         /* SIOCGIWRETRY */

rtw_wx_set_enc,                 /* SIOCSIWENCODE */

rtw_wx_get_enc,                 /* SIOCGIWENCODE */

rtw_wx_get_power,             /* SIOCGIWPOWER */

rtw_wx_set_gen_ie,             /* SIOCSIWGENIE */

rtw_wx_set_auth,         /* SIOCSIWAUTH */

rtw_wx_set_enc_ext,           /* SIOCSIWENCODEEXT */

rtw_wx_set_pmkid,              /* SIOCSIWPMKSA */

 

下面根據簡單的幾個ioctl來分析下,主要流程看下圖就可以了。

----------------------------------------------------華麗的分割線----------------------------------------------



關於wifi的連接過程

----------------------------------------------------------------------------------------------------------------------

Wifi連接過程主要是先由應用層調用SIOCSIWESSID這個命令,然後根據sdio wifi通過幀數據來交互認真,連接等。主要流程如下:

----------------------------------------------------華麗的分割線----------------------------------------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章