无线驱动移植与开发-MTK源码分析

引子

MTKdirver起来,上层的操作过程一般有如下几个过程:

  1. 修改无线配置文件,例如 /var/Wireless/RT2860AP/RT2860AP.dat,可以看作是hostapd.conf
  2. 修改功率限制问题,/var/Wireless/RT2860AP/SingleSKU.dat,防止功率超过硬件发射限制,导致烧PA
  3. 修改EEPROM,主要是修改MAC,无线发射功率等,具体值可参考MTXXX_EEPROM_Guideline
  4. insmod mt-wifi.ko
  5. ifconfig ra0 up
  6. brctl addif br0 ra0

接下来主要是从驱动源码来追述以上步骤

insmod mt-wifi.ko

直接在driver中搜索module_init,下面以M7628的源码为准

int __init rt2880_module_init(void)
{
	....
	RtmpRaDevCtrlInit(pAd, RTMP_DEV_INF_RBUS);//初始化 PRTMP_ADAPTER
	....
	net_dev = RtmpPhyNetDevInit(pAd, &netDevHook);//初始化pDevOpHook
	....
	rv = RtmpOSNetDevAttach(pAd->OpMode, net_dev, &netDevHook);//初始化netdev 的ops
	....
}

RtmpOSNetDevAttach中初始化了无线网络设备的operation,比较重要的函数如下

pNetDevOps->ndo_open = MainVirtualIF_open;
pNetDevOps->ndo_stop = MainVirtualIF_close;
pNetDevOps->ndo_start_xmit =rt28xx_send_packets;
pNetDevOps->ndo_do_ioctl = rt28xx_ioctl;
pNetDev->wireless_handlers = rt28xx_ap_iw_handler_def;
if (rtnl_locked)
	ret = register_netdevice(pNetDev);
else
	ret = register_netdev(pNetDev);

netif_stop_queue(pNetDev);

最后register_netdevice 注册网络设备以及netif_stop_queue 停止队列,netdevice的名称需要在RtmpPhyNetDevMainCreate修改,这个函数是由RtmpPhyNetDevInit调用的

ifconfig ra0 up

这句命令调用的是pNetDevOps->ndo_open,也就是MainVirtualIF_open:

int MainVirtualIF_open(struct net_device *net_dev)
{
...
	if (VIRTUAL_IF_UP(pAd) != 0)//继续up interface
		return -1;
....
//开启网络队列
	netif_start_queue(net_dev);
	netif_carrier_on(net_dev);
	netif_wake_queue(net_dev);

	return 0;
}

VIRTUAL_IF_UP通过ioctl 调用 rt28xx_open

int rt28xx_open(VOID *dev)
{
...
	RtmpOSIRQRequest(net_dev);//request irq 

	RTMP_DRIVER_IRQ_INIT(pAd);
	if (rt28xx_init(pAd, mac, hostname) == FALSE)//init 驱动
		goto err;
....
/*以下都是其他wifi 接口的初始化,暂不提*/
	RT28xx_MBSS_Init(pAd, net_dev);//Multi BSS
	RT28xx_WDS_Init(pAd, net_dev);// WDS fuction
	RT28xx_ApCli_Init(pAd, net_dev);//ap client
	RT28xx_Monitor_Init(pAd, net_dev);//sniffer
....
	RTMPDrvOpen(pAd);//开启驱动底层,硬件相关的txrx
...
}

以上有三个比较重要的函数:

  1. RtmpOSIRQRequest 申请软中断,主要用于收包
  2. rt28xx_init 初始化驱动,读取配置文件和EEPROM就是在这个函数中
  3. RTMPDrvOpen 开启终端,打开硬件开关等

下面先从rt28xx_init开始看

int rt28xx_init(VOID *pAdSrc, RTMP_STRING *pDefaultMac, RTMP_STRING *pHostName)
{

	Status = RtmpNetTaskInit(pAd);//注册数据包kernel work

	RtmpMgmtTaskInit(pAd);//注册管理帧的kernel work

	/* initialize MLME*/
	Status = MlmeInit(pAd);

	if (rtmp_cfg_init(pAd, pHostName) != TRUE)//读取配置文件
		goto err5;

	if (MCUSysInit(pAd) != NDIS_STATUS_SUCCESS)//load firmware
		goto err6;

	RtmpChipOpsEepromHook(pAd, pAd->infType,E2P_NONE);//初始化chipops,为了读取EEPROM做准备

	NICReadEEPROMParameters(pAd, (RTMP_STRING *)pDefaultMac);//读取EEPROM

	APStartUp(pAd);//初始化wdev,实际的发包函数

	MlmeRadioOn(pAd);
}

RtmpNetTaskInit 以数据包为例,例如初始化了rx 的tasklet:

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->dfs_task, dfs_tasklet, (unsigned long)pAd);

rtmp_cfg_init 读取/var/Wireless/RT2860AP/RT2860AP.dat 和 /var/Wireless/RT2860AP/SingleSKU.dat,配置无线

RTMPReadParametersHook //读取AP_PROFILE_PATH_RBUS 中的配置参数,可以看作是hostapd.conf
RTMPSetSingleSKUParameters //读取SINGLE_SKU_TABLE_FILE_NAME,存放发射功率上限

NICReadEEPROMParameters从flash或者eeprom中读取校准eeprom 值,具体在RtmpChipOpsEepromHook定义,调用rtmp_nv_init,之后将eeprom中的值配置到无线中

NDIS_STATUS rtmp_nv_init(RTMP_ADAPTER *pAd)
{
    spiflash_ioctl_read(pAd->eebuf, DEFAULT_RF_OFFSET, EEPROM_SIZE);	
    rtmp_ee_flash_init(pAd, pAd->eebuf);//flash中配置有问题就从默认EEPROM文件中读取    EEPROM_DEFAULT_FILE_PATH,存放mac,无线校准值等
}

APStartUp 中调用wdev_init,初始化wdev,可以看到发包的实际函数APHardTransmit

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;

至此整个驱动底层到网络层都已配置完成,收发包队列也已开启,接下来简单的分析下收发包代码

ra0 发包    

网络设备ra0收到协议栈发的包后,首先进入pNetDevOps->ndo_start_xmit =rt28xx_send_packets;不考虑驱动的forward功能等,直接进到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;
    }
	if (allowToSend == TRUE)
	{
        if (wdev->tx_pkt_handle)
	        wdev->tx_pkt_handle(pAd, pPacket);
    }
	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)
{
    sta_num=0;
    Total_Packet_Number = 0 ;
    for (i=0; iMacTab.Content[i];
         tr_entry_tmp = &pAd->MacTab.tr_entry[pEntry->wcid];

         /* dump all MacTable entries */
         if (pEntry->EntryType == ENTRY_NONE)
              continue;
 
         sta_num++;
 
         Total_Packet_Number = Total_Packet_Number + tr_entry_tmp->ps_queue.Number;
         for (j = 0; j < WMM_QUE_NUM; j++)
             Total_Packet_Number = Total_Packet_Number + tr_entry_tmp->tx_queue[j].Number;
                                                                   
    }
 //检查所有关联客户端的报文,如果超过1024且大于3个客户端,则drop该报文
    if (Total_Packet_Number > 1024 && sta_num > 3)
    {
         //DBG_WARN("too many packets cached in all tr_entry, do not enq 0x%08x\n", pPacket);
         drop_reason = 12;
         goto drop_pkt;
    }
//决定WMM队列
	wdev = tr_entry->wdev;
	UserPriority = 0;
	QueIdx = QID_AC_BE;
	if (RTMPCheckEtherType(pAd, pPacket, tr_entry, wdev, &UserPriority, &QueIdx) == FALSE) {
		drop_reason = INVALID_ETH_TYPE;
		goto drop_pkt;
	}
//分片包处理
	RTMP_SET_PACKET_FRAGMENTS(pPacket, NumberOfFrag);
//该WMM 优先级队列超过长度后,将网络接口停止收报,一般设为512
	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;
		}
	}
//将该报文入队
    rtmp_enq_req(pAd, pPacket, QueIdx, tr_entry, FALSE,NULL);
}

报文入队后,调用RTMPDeQueuePacket 对各队列中的报文进行发包处理,其中实际调用的发包函数为wdev_init中定义的APHardTransmit,驱动实际的发包函数发往硬件

inline VOID _RTMPDeQueuePacket(
	IN RTMP_ADAPTER *pAd,
	IN BOOLEAN in_hwIRQ,
	IN UCHAR QIdx,
	IN INT wcid,
	IN INT max_cnt)
{
	do
	{
		DEQUEUE_LOCK(&pAd->irq_lock, in_hwIRQ, IrqFlags);//加锁
//取队列中的报文,没取到跳出循环
		rtmp_deq_req(pAd, max_cnt, &deq_info);
		DEQUEUE_UNLOCK(&pAd->irq_lock, in_hwIRQ, IrqFlags);//释放锁	

		RTMP_START_DEQUEUE(pAd, QueIdx, IrqFlags,deq_info);

		DEQUEUE_LOCK(&pAd->irq_lock, in_hwIRQ, IrqFlags);

		deq_packet_gatter(pAd, &deq_info, pTxBlk, in_hwIRQ);
		
		DEQUEUE_UNLOCK(&pAd->irq_lock, in_hwIRQ, IrqFlags);
		if (pTxBlk->wdev && pTxBlk->wdev->wdev_hard_tx) {
			pTxBlk->wdev->wdev_hard_tx(pAd, pTxBlk);//调用APHardTransmit
		}
	}while(1);

}

ra0收包

之前在ra0 open的时候我们讲到 调用RtmpOSIRQRequest注册了软中断

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

硬件收到包之后会触发软中断处理函数rt2860_interrupt

VOID RTMPHandleInterrupt(VOID *pAdSrc)
{

	/* Inital the Interrupt source. */
	IntSource = 0x00000000L;//终端源,根据这个来调用各种kernel tasklet

	if (pAd->chipCap.hif_type == HIF_MT) {
		RTMP_IO_READ32(pAd, MT_INT_SOURCE_CSR, &IntSource);

//针对rx数据包接收来说
	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);//关闭rx接受中断
			RTMP_OS_TASKLET_SCHE(&pObj->rx_done_task);//schedule rx的tasklet
		}
		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)
{
    //从rxring中拿取报文
	pRxPacket = GetPacketFromRxRing(pAd, pRxBlk, &bReschedule, &RxPending, 0);
//从报文type选择不同的rx 函数
	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

参考文档

MT768_EEPROM_Guideline

你可能感兴趣的:(WiFi,MTK)