更好分析无线驱动代码在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
....
}
以上有三个比较重要的函数:
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()