MTK的無線驅動代碼流程分析

MTK無線驅動TX和RX分析

介紹

更好分析無線驅動代碼在gitbub下的源代碼:https://github.com/chaokw/mt7603e-p4rev-112670.git,有興趣的可以下載下來看下
文章轉自https://blog.csdn.net/u011212816/article/details/81137126

驅動流程

接下來直接講述驅動的流程,加載內核模塊ko之後會代碼的入口xx_init_module_xx或者xx_module_init_xx函數作爲代碼初始入口

/*
	Driver module load/unload function
*/
int __init rt_pci_init_module(void)
{
	DBGPRINT(RT_DEBUG_ERROR, ("register %s\n", RTMP_DRV_NAME));

	/*add for initial hook callback function linking list*/
	RTMP_OS_TXRXHOOK_INIT();

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
	return pci_register_driver(&rt_pci_driver);
#else
    return pci_module_init(&rt_pci_driver);
#endif
}

由代碼可見爲PCI的驅動,主要查看rt_pci_driver中的.probe

/*
	PCI device probe & initialization function
*/
static int DEVINIT rt_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
{
	...
	/* get DRIVER operations */
	RTMP_DRV_OPS_FUNCTION(pRtmpDrvOps, NULL, &PciConfig, NULL);
	....
	/*NetDevInit============================================== */
	net_dev = RtmpPhyNetDevInit(pAd, &netDevHook);
	rv = RtmpOSNetDevAttach(OpMode, net_dev, &netDevHook);
	....
}

RtmpPhyNetDevInit主要對網絡設備初始化,對設備的operation進行賦值

	pNetDevHook->open = MainVirtualIF_open;
	pNetDevHook->stop = MainVirtualIF_close;
	pNetDevHook->xmit = rt28xx_send_packets;
	pNetDevHook->ioctl = rt28xx_ioctl;
	pNetDevHook->priv_flags = InfId; /*INT_MAIN; */
	pNetDevHook->get_stats = RT28xx_get_ether_stats;
	...

RtmpOSNetDevAttach主要設置適配內核的網絡設備struct net_device

	pNetDev->open = pDevOpHook->open;
	pNetDev->stop = pDevOpHook->stop;
	pNetDev->hard_start_xmit =
		(HARD_START_XMIT_FUNC) (pDevOpHook->xmit);
	pNetDev->do_ioctl = pDevOpHook->ioctl;
	....
	ret = register_netdev(pNetDev);
	netif_stop_queue(pNetDev);

應用層命令調用ifconfig wlanX(ra) up無線接口,則會調用MainVirtualIF_open接口

int MainVirtualIF_open(struct net_device *net_dev)
{
....
	if (VIRTUAL_IF_UP(pAd) != 0)
		return -1;
....
//開啓網絡隊列
	netif_start_queue(net_dev);
	netif_carrier_on(net_dev);
	netif_wake_queue(net_dev);

	return 0;
}

MainVirtualIF_open函數會通過ioclt最終調用rt28xx_open函數

int rt28xx_open(VOID *dev)
{
....
	/*
		Request interrupt service routine for PCI device
		register the interrupt routine with the os

		AP Channel auto-selection will be run in rt28xx_init(),
		so we must reqister IRQ hander here.
	*/
//開啓硬件中斷處理,主要RX
	RtmpOSIRQRequest(net_dev);

	/* Init IRQ parameters stored in pAd */
/*	rtmp_irq_init(pAd); */
	RTMP_DRIVER_IRQ_INIT(pAd);

	/* Chip & other init */
//主要設置mac以及數據幀處理定時器初始化
	if (rt28xx_init(pAd, mac, hostname) == FALSE)
		goto err;

//無線設置相關
	RT28xx_MBSS_Init(pAd, net_dev);
	RT28xx_WDS_Init(pAd, net_dev);
	RTMP_DRIVER_CFG80211_START(pAd);
....
	RTMPDrvOpen(pAd);//開啓驅動底層,硬件相關的txrx
....
}

以上有三個比較重要的函數:

  1. RtmpOSIRQRequest 註冊中斷服務,主要處理無線網卡的硬件中斷用於收包
  2. rt28xx_init 初始化驅動,讀取配置文件和EEPROM就是在這個函數中
  3. RTMPDrvOpen 開啓終端,打開硬件開關等
int rt28xx_init()
{
....
	Status = RtmpNetTaskInit(pAd);//註冊數據包kernel work
....	
	Status = RtmpMgmtTaskInit(pAd);//註冊管理幀kernel work
	/* initialize MLME*/
	Status = MlmeInit(pAd);

	if (rtmp_cfg_init(pAd, pHostName) != TRUE)//讀取配置文件.dat

	if (MCUSysInit(pAd) != NDIS_STATUS_SUCCESS)//load firmware

	/* hook e2p operation */
	RtmpChipOpsEepromHook(pAd, pAd->infType,E2P_NONE);//初始化chipops,爲了讀取EEPROM做準備
	
	/* We should read EEPROM for all cases */
	NICReadEEPROMParameters(pAd, (RTMP_STRING *)pDefaultMac);//讀取EEPROM

	APStartUp(pAd);//初始化wdev,實際的發包函數
	
	MlmeRadioOn(pAd);

}

RtmpNetTaskInit函數

NDIS_STATUS RtmpNetTaskInit(RTMP_ADAPTER *pAd)
{
....
	RTMP_OS_TASKLET_INIT(pAd, &pObj->rx_done_task, rx_done_tasklet, (unsigned long)pAd);
....
	RTMP_OS_TASKLET_INIT(pAd, &pObj->bcn_dma_done_task, bcn_dma_done_tasklet, (unsigned long)pAd);
	RTMP_OS_TASKLET_INIT(pAd, &pObj->mt_mac_int_0_task, mt_mac_int_0_tasklet, (unsigned long)pAd);
	RTMP_OS_TASKLET_INIT(pAd, &pObj->mt_mac_int_1_task, mt_mac_int_1_tasklet, (unsigned long)pAd);
	RTMP_OS_TASKLET_INIT(pAd, &pObj->mt_mac_int_2_task, mt_mac_int_2_tasklet, (unsigned long)pAd);
	RTMP_OS_TASKLET_INIT(pAd, &pObj->mt_mac_int_3_task, mt_mac_int_3_tasklet, (unsigned long)pAd);
	RTMP_OS_TASKLET_INIT(pAd, &pObj->mt_mac_int_4_task, mt_mac_int_4_tasklet, (unsigned long)pAd);
	RTMP_OS_TASKLET_INIT(pAd, &pObj->bmc_dma_done_task, bmc_dma_done_tasklet, (unsigned long)pAd);
	RTMP_OS_TASKLET_INIT(pAd, &pObj->mgmt_dma_done_task, mgmt_dma_done_tasklet, (unsigned long)pAd);
....
	RTMP_OS_TASKLET_INIT(pAd, &pObj->dfs_task, dfs_tasklet, (unsigned long)pAd);
....
}

rtmp_cfg_init 讀取/var/Wireless/RT2860AP/RT2860AP.dat,配置無線

RTMPReadParametersHook //讀取AP_PROFILE_PATH_RBUS 中的配置參數,可以看作是hostapd.conf

NICReadEEPROMParameters從flash或者eeprom中讀取校準eeprom 值,具體在RtmpChipOpsEepromHook定義,調用rtmp_nv_init,之後將eeprom中的值配置到無線中

NDIS_STATUS rtmp_nv_init(RTMP_ADAPTER *pAd)
{
....
	RtmpFlashRead(pAd->eebuf, pAd->flash_offset, EEPROM_SIZE);
....
	return rtmp_ee_flash_init(pAd, pAd->eebuf);
}

APStartUp 中調用wdev_init,初始化wdev,可以看到發包的實際函數APHardTransmit

INT wdev_init(RTMP_ADAPTER *pAd, struct wifi_dev *wdev, UINT wdev_type)
{
....
	wdev->wdev_type = WDEV_TYPE_AP;

	wdev->tx_pkt_allowed = ApAllowToSendPacket;
	wdev->tx_pkt_handle = APSendPacket;
	wdev->wdev_hard_tx = APHardTransmit;
		
	wdev->rx_pkt_allowed = ap_rx_pkt_allow;
	wdev->rx_ps_handle = ap_rx_ps_handle;
	wdev->rx_pkt_foward = ap_rx_foward_handle;

	pMbss = (BSS_STRUCT *)wdev->func_dev;
....
}

無線收發包pNetDev->hard_start_xmit調用pNetDevHook->xmit = rt28xx_send_packets->rt28xx_packet_xmit->RTMPSendPackets->wdev_tx_pkts

INT wdev_tx_pkts(NDIS_HANDLE dev_hnd, PPNDIS_PACKET pkt_list, UINT pkt_cnt, struct wifi_dev *wdev)
{
....

	if (((wdev->allow_data_tx == TRUE) && (wdev->tx_pkt_allowed)) {
		allowToSend = wdev->tx_pkt_allowed(pAd, wdev, pPacket, &wcid);
	}
	else {
		allowToSend = FALSE;
		//RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_FAILURE);
	}

	if (allowToSend == TRUE)
	{
		if (wdev->tx_pkt_handle)
			wdev->tx_pkt_handle(pAd, pPacket);
		else
		{
			DBGPRINT(RT_DEBUG_ERROR, ("%s():tx_pkt_handle not assigned!\n", __FUNCTION__));
			RELEASE_NDIS_PACKET(pAd, pPacket, NDIS_STATUS_FAILURE);
		}
	}

	RTMPDeQueuePacket(pAd, FALSE, NUM_OF_TX_RING, WCID_ALL, MAX_TX_PROCESS);
....
}

wdev_tx_pkts中比較重要的函數有三個:tx_pkt_allowed;tx_pkt_handle;RTMPDeQueuePacket
前兩個函數是在之前APStartUp 中調用wdev_init 初始化的,分別對應的函數是

wdev->tx_pkt_allowed = ApAllowToSendPacket;
wdev->tx_pkt_handle = APSendPacket;

ApAllowToSendPacket檢查目的mac是否是關聯的設備,如果是就允許,不是就不允許

INT APSendPacket(RTMP_ADAPTER *pAd, PNDIS_PACKET pPacket)
{
....
	RTMP_SET_PACKET_FRAGMENTS(pPacket, NumberOfFrag);

	if (pAd->TxSwQueue[QueIdx].Number >= pAd->TxSwQMaxLen)
	{
		{
#ifdef BLOCK_NET_IF
			StopNetIfQueue(pAd, QueIdx, pPacket);
#endif /* BLOCK_NET_IF */
			drop_reason = DROP_TXQ_FULL;
			goto drop_pkt;
		}
	}

		if (rtmp_enq_req(pAd, pPacket, QueIdx, tr_entry, FALSE,NULL) == FALSE) {
			drop_reason = DROP_TXQ_ENQ_FAIL;
			goto drop_pkt;
		}
....
}

報文入隊後,調用RTMPDeQueuePacket 對各隊列中的報文進行發包處理,其中實際調用的發包函數爲wdev_init中定義的APHardTransmit,驅動實際的發包函數發往硬件

VOID RTMPDeQueuePacket()
{
....

	do
	{
		DEQUEUE_LOCK(&pAd->irq_lock, in_hwIRQ, IrqFlags);//加鎖
		rtmp_deq_req(pAd, max_cnt, &deq_info);

		RTMP_START_DEQUEUE(pAd, QueIdx, IrqFlags);
		deq_packet_gatter(pAd, &deq_info, pTxBlk, in_hwIRQ);
		if (pTxBlk->wdev && pTxBlk->wdev->wdev_hard_tx) {
			pTxBlk->wdev->wdev_hard_tx(pAd, pTxBlk);//調用APHardTransmit
		}
		RTMP_STOP_DEQUEUE(pAd, QueIdx, IrqFlags);
		rtmp_deq_report(pAd, &deq_info);
		DEQUEUE_UNLOCK(&pAd->irq_lock, in_hwIRQ, IrqFlags);//解鎖

		if (round >= 1024) {//最多循環1024
			DBGPRINT(RT_DEBUG_OFF, ("%s():ForceToBreak!!Buggy here?\n", __FUNCTION__));
			break;
		}
	}while(1);
....
}

無線收包
之前在無線接口 open的時候我們講到 調用RtmpOSIRQRequest註冊了中斷服務

request_irq(pci_dev->irq,  rt2860_interrupt, SA_SHIRQ, (net_dev)->name, (net_dev));

硬件收到包之後會觸發中斷處理函數rt2860_interrupt->RTMPHandleInterrupt

VOID RTMPHandleInterrupt(VOID *pAdSrc)
{
....
		if (pAd->chipCap.hif_type == HIF_MT)
		{
			/* Fix Rx Ring FULL lead DMA Busy, when DUT is in reset stage */
        		IntSource = IntSource & (MT_INT_CMD | MT_INT_RX | WF_MAC_INT_3);
		}
....
	if (IntSource & INT_RX_DATA)
	{
		pAd->RxDMACheckTimes = 0;
		pAd->RxPseCheckTimes = 0;
		if ((pAd->int_disable_mask & INT_RX_DATA) == 0)
		{
			rt2860_int_disable(pAd, INT_RX_DATA);
			RTMP_OS_TASKLET_SCHE(&pObj->rx_done_task);
		}
		pAd->int_pending |= INT_RX_DATA;
	}
....
}

而rx_done_task 在 RtmpNetTaskInit中已經定義了tasklet 的function:rx_done_tasklet,調用rtmp_rx_done_handle

BOOLEAN rtmp_rx_done_handle(RTMP_ADAPTER *pAd)
{
....
	pRxPacket = GetPacketFromRxRing(pAd, pRxBlk, &bReschedule, &RxPending, 0);
....
	switch (pHeader->FC.Type)
	{
		case FC_TYPE_DATA:
			dev_rx_data_frm(pAd, &rxblk);
			break;

		case FC_TYPE_MGMT:
			dev_rx_mgmt_frm(pAd, &rxblk);
			break;

		case FC_TYPE_CNTL:
			dev_rx_ctrl_frm(pAd, &rxblk);
			break;

		default:
			RELEASE_NDIS_PACKET(pAd, pRxPacket, NDIS_STATUS_FAILURE);
			break;
	}
....
}

dev_rx_data_frm函數的調用棧如下,到達netif_rx就到了網絡協議棧了,這裏沒考慮ampdu,forward等情況,只是簡單列下調用過程

dev_rx_data_frm()
    ---rx_data_frm_announce()
        ---Indicate_Legacy_Packet()
            ---Announce_or_Forward_802_3_Packet()
                ---announce_802_3_packet()
                    ---RtmpOsPktRcvHandle()
                        ---netif_rx()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章