和菜鳥一起學linux之wifi學習記錄
不說這些了,還是把上次總結的wifi驅動給分析分析吧。
Sdio wifi,首先,他是一個sdio的卡的裝置,然後具備了wifi的功能,所以,註冊的時候還是先以sdio的卡的裝置去註冊的。然後檢測到卡之後就要驅動他的wifi功能了,顯然,他是用sdio的協議,通過發命令和資料來控制的。雖然wifi資料的傳輸流程,但是也得知道他是怎麼工作的,要不然連函式指標在哪都找不到,那就更別說其他的了。好了,還是先從初始化開始吧。
----------------------------------------------------華麗的分割線------------------------------------------------
關於sdio初始化
----------------------------------------------------------------------------------------------------------------------
static int __init rtw_drv_entry(void)
這裡做了一些初始化工作,主要是:
ret = sdio_register_driver(&sdio_drvpriv.r871xs_drv);
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->hal_init = &rtl8723as_hal_init;
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_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通過幀資料來互動認真,連線等。主要流程如下:
----------------------------------------------------華麗的分割線----------------------------------------------