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);
#elsereturn 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 deviceregister the interrupt routine with the osAP Channel auto-selection will be run in rt28xx_init(),so we must reqister IRQ hander here.*/
//开启硬件中断处理,主要RXRtmpOSIRQRequest(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)//读取配置文件.datif (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);//读取EEPROMAPStartUp(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_IFStopNetIfQueue(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) {//最多循环1024DBGPRINT(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()

MTK的无线驱动代码流程分析相关推荐

  1. netts之 CTWSocket代码流程分析(整体是客户端请求式的)

    netts之 CTWSocket代码流程分析(整体是客户端请求式的) 1.外部程序载入调用 netts.dll 2.外部程序调用 netts实现的Stock_Init_Nodialog,(Stock_ ...

  2. android加载efi分区,高通Android UEFI XBL 代码流程分析

    高通Android UEFI XBL 代码流程分析 背景 之前学习的lk阶段点亮LCD的流程算是比较经典,但是高通已经推出了很多种基于UEFI方案的启动架构. 所以需要对这块比较新的技术进行学习.在学 ...

  3. android uefi 编译报错,【Android SDM660开机流程】- UEFI XBL 代码流程分析

    [Android SDM660开机流程]- UEFI XBL 代码流程分析 一.UEFI XBL 1.1 boot_images代码目录 1.2 UEFI代码运行流程 1.3 SEC (安全验证) 1 ...

  4. 【SemiDrive源码分析】【X9芯片启动流程】26 - R5 SafetyOS 之 LK_INIT_LEVEL_TARGET 阶段代码流程分析(TP Drvier、Audio Server初始化)

    [SemiDrive源码分析][X9芯片启动流程]26 - R5 SafetyOS 之 LK_INIT_LEVEL_TARGET 阶段代码流程分析(TP Drvier.Audio Server初始化) ...

  5. 全志 android 编译,全志A20启动代码流程分析 ——Android

    现在的CPU都固化了内部 ROM,内部 ROM中有一般都有一段程序,一般有如下几个功能: 1,初始化,部分外设,如USB,SDCARD 2,初始化DDR(内存)和NandFlash 3,加载boot( ...

  6. 全志android 编译,全志A20启动代码流程分析 ——Android

    现在的CPU都固化了内部 ROM,内部 ROM中有一般都有一段程序,一般有如下几个功能: 1,初始化,部分外设,如USB,SDCARD 2,初始化DDR(内存)和NandFlash 3,加载boot( ...

  7. Slub代码流程分析

    Slub代码流程分析: slub的代码晦涩难懂,在看书或者相关资料时看似简单,再去对照代码分析时会发现被打回原形,就像数学老师推导公式和自己去推导公式一样.因此,需要静下来心来仔细研读,看完原理之后分 ...

  8. atheros 无线驱动接收流程

    atheros 无线驱动接收流程如下: request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc); irqretur ...

  9. Android原生音频变调代码流程分析

    会说话的Tom猫是一款非常经典的终端游戏,可爱的Tom猫可以发出不同音调的声音. 之前用过一个非常著名的开源库SoundTouch可以实现音频的变速变调功能,具体可参考: https://blog.c ...

最新文章

  1. 商汤再挖MSRA大将:R-FCN作者代季峰加盟任执行研究总监
  2. FIlterInputStream和FilterOutputStream
  3. 根据浏览器内核判断是web/iOS/android/ipad/iphone 来打开不同的网站或页面
  4. videoview 播放视频
  5. Linux下计划任务:crontab 命令的权限说明
  6. 计算机图形学在数学中的应用,计算机图形学的数学工具与C#实现:数学C
  7. vant 软键盘_移动端页面输入底部被软键盘遮挡问题
  8. 在EXCEL中如何将一列中的相同值的数据行找出来?
  9. 1006 换个格式输出整数 (15 分)(c语言)
  10. 解决在ascx使用outputcache就不可以设置用户控件自己的属性
  11. koa中间件机制详解
  12. gocd_如何将DangerJS集成到GoCD管道中
  13. pytorch学习笔记(二十一):Channels
  14. 探讨绝对哲学存在的必要条件
  15. Openwrt netifd ubus解析
  16. pdfminer将pdf转为csv
  17. 企信下载的文件在哪里_Foobar2000(无损音乐播放器下载)(软件篇)
  18. 使用dd命令测试裸盘性能评测
  19. 无线测温产品在轧钢厂项目中的应用
  20. 为什么我不看好人人网在美国上市

热门文章

  1. css精灵图(雪碧图)切图
  2. Flutter PDF Android 电子签章不显示问题
  3. 【EI会议 | 即将截稿】第二届机器人、自动化与智能控制国际会议征稿中
  4. Ghost安装windows系统出现A:\GHOSTERR.TXT的解决办法
  5. 闻一以知十,前端要会的10道面试题(附答案与解析)
  6. DjangoRestFramework【DRMBBasic认证】
  7. 尼姆游戏(人机对战)的Python实现
  8. 旅行拍照时脸背光怎么办?如何拍出…
  9. win10下关闭笔记本自带键盘以及解锁
  10. mysql测试数据库employees一些sql语句