【WiFi软件开发】mtk驱动代码学习


前言

近期对mtk的7916驱动代码进行了学习,本文对就此进行简单介绍和解读。


一、驱动模块编译

1.编译入口

先看下驱动的编译。编译入口为drivers/MT7916/Makefile,编译mt7916.ko的起始代码如下:

$(MAKE) ARCH=$(ARCH) -C $(LINUX_SRC)  M=$(MTK_MAKE_DIR) modules
$(CROSS_COMPILE)strip --strip-unneeded $(MTK_MAKE_DIR)/$(MODULE_NAME).ko

这两行是在linux下编译可加载内核模块形成.ko文件的makefile中的核心语句,

第一行相当于在当前系统内核根目录$(LINUX_SRC)下执行make M=$(MTK_MAKE_DIR) modules 进行MTK_MAKE_DIR目录下的模块的编译,编译成xx.ko.这样你的modules就可以在任何其他位置,而不用必须在内核文件下面了。代码中各变量依次解释如下:

  • $(MAKE) 是make命令的变量,表示调用make工具来执行后续的命令。
  • ARCH=$(ARCH) 将makefile中的ARCH变量的值设置为当前的架构类型。
  • -C $(LINUX_SRC) 告诉make命令切换到指定的目录(即Linux源代码的目录),然后在该目录下执行后续的命令。
  • M=$(MTK_MAKE_DIR) 设置一个名为M的变量,其值为另一个makefile文件的路径。
  • modules 是make命令的一个参数,表示构建内核模块。

所以,这整行代码的含义是:使用make命令在指定的Linux源代码目录下,针对特定的架构类型,构建由MTK_MAKE_DIR变量指向的makefile文件定义的内核模块。

第二行代码是在进行Linux内核模块的处理。

  • $(CROSS_COMPILE)strip是一个用于去除编译后的可执行文件或库文件中不需要的部分的工具。
  • --strip-unneeded选项告诉strip命令去除那些未被引用的函数和变量,以减小文件大小。
  • $(MTK_MAKE_DIR)/$(MODULE_NAME).ko是要处理的目标文件的路径。

整体来说,这段代码是在去除编译后的内核模块中的未使用部分,以优化其大小。

2.配置文件

MTK_MAKE_DIR指向的是目录drivers/MT7916/mt_wifi_ap,该目录下有编译ko模块需要的Makefile和Kconfig文件。

这里简单介绍下ko变异中常用的几个文件:Makefile、Kconfig和.config文件:简单来说就是去饭店点菜:Kconfig是菜单,Makefile是做法,.config就是你点的菜。

 - Makefile:一个文本形式的文件,编译源文件的方法。
 - Kconfig:一个文本形式的文件,内核的配置菜单。(宏的声明)
 - .config:编译内核所依据的配置,通常由Kconfig进行配置生成。(宏的定义)

因此配置内核方式有3种(定义宏的3种方式),任选其一:

1)make menuconfig
(2)make xxx_defconfig
(3)直接修改.config

回到驱动编译中来,以mt7916为例:

**drivers/MT7916/mt_wifi_ap/Kconfig**
config MT_AP_SUPPORT
**drivers/MT7916/mt_wifi_ap/mtk_config**
CONFIG_MT_AP_SUPPORT=m
**drivers/MT7916/mt_wifi_ap/Makefile**
obj-$(CONFIG_MT_AP_SUPPORT) += $(DRV_NAME).o

Kconfig中对CONFIG_MT_AP_SUPPORT进行了声明,mtk_config定义了该宏为mMakefile中基于该宏决定.o文件的编译。

这里给出几种Makefile中文件的编译方法:

 1. 直接编译:表示由xxx.c或xxx.s编译得到xxx.o并直接编进内核。
 obj-y      +=xxx.o   

 2. 条件编译:根据.config文件的CONFIG_XXX来决定文件是否编进内核。
obj -$(CONFIG_HELLO)  +=xxx.o   

 3. 模块编译:表示xxx作为模块编译,即执行make modules时才会被编译。
obj-m     +=xxx.o

显然,驱动中$(DRV_NAME).o的编译属于第三种。

3.整体编译流程

基于以上学习,mt7916驱动的整体编译流程可简单梳理为下:

$(MAKE) ARCH=$(ARCH) -C $(LINUX_SRC)  M=$(MTK_MAKE_DIR) modules		//mt7916.ko编译
    config MT_AP_SUPPORT
    CONFIG_MT_AP_SUPPORT=m
    obj-$(CONFIG_MT_AP_SUPPORT) += $(DRV_NAME).o		//mt7916.o编译
        $(DRV_NAME)-objs += $(ap_objs) $(cmm_objs) $(asic_objs) $(chip_objs) $(rate_objs)\
                            $(spec_objs) $(func_objs) $(os_objs) $(dot11_ft_objs) $(serv_objs)
            ap_objs += $(SRC_EMBEDDED_DIR)/../os/linux/multi_main_dev.o			//选择参与编译的.o文件
            os_objs += $(SRC_DIR)/os/linux/multi_main_dev.o						//选择参与编译的.o文件

4.新增文件的编译方法

如果需要在驱动中添加新文件参与编译,该如何操作呢?其实上文已经给出了方法,只需要将目标.o文件加入$(DRV_NAME)-objs即可:

$(DRV_NAME)-objs += $(ap_objs) $(cmm_objs) $(asic_objs) $(chip_objs) $(rate_objs)\
                    $(spec_objs) $(func_objs) $(os_objs) $(dot11_ft_objs) $(serv_objs)

ap_objs := $(SRC_EMBEDDED_DIR)/ap/ap.o\										//选择参与编译的.o文件
            $(SRC_EMBEDDED_DIR)/ap/ap_mlme.o\
            $(SRC_EMBEDDED_DIR)/ap/ap_sanity.o\
            $(SRC_EMBEDDED_DIR)/ap/ap_wpa.o\
            $(SRC_EMBEDDED_DIR)/ap/ap_sec.o\
            $(SRC_EMBEDDED_DIR)/ap/ap_data.o\
            $(SRC_EMBEDDED_DIR)/ap/ap_autoChSel.o\
            $(SRC_EMBEDDED_DIR)/ap/ap_qload.o\
            $(SRC_EMBEDDED_DIR)/ap/ap_cfg.o\
            $(SRC_EMBEDDED_DIR)/ap/ap_vow.o\
            $(SRC_DIR)/os/linux/ap_ioctl.o\
            $(SRC_EMBEDDED_DIR)/fsm/ap_mgmt_cntl.o\
            $(SRC_EMBEDDED_DIR)/fsm/ap_mgmt_sync.o\
	    $(SRC_EMBEDDED_DIR)/fsm/ap_mgmt_auth.o\
            $(SRC_EMBEDDED_DIR)/fsm/ap_mgmt_assoc.o\
            $(SRC_EMBEDDED_DIR)/ap/ap_bss_mgmt.o

cmm_objs := $(SRC_EMBEDDED_DIR)/common/action.o\
		...
		$(SRC_DIR)/new_func.o										//选择新增的.o文件

二、驱动初始化流程

驱动以ko模块的形式启动,接下来给出驱动启动时的初始化流程:

wifi_drv_init_module
    rt_pci_init_module              //加载ko
        rt_pci_driver
            rt_pci_probe
                mt_pci_dev_set(pdev, &csr_addr)         //使能设备,从配置区的基址寄存器取得IO区域基址,并映射为虚拟地址
                rv = RTMPAllocAdapterBlock(handle, (VOID **)&pAd, RTMP_DEV_INF_PCIE);           //pAdapter 内存申请
                    Status = AdapterBlockAllocateMemory(handle, (PVOID *)&pAd, sizeof(RTMP_ADAPTER));
                RTMP_DRIVER_PCI_CSR_SET(pAd, csr_addr);
                RTMP_DRIVER_PCIE_INIT(pAd, &pci_config, pdev);
                net_dev = RtmpPhyNetDevInit(pAd, &netDevHook);      //创建和初始化主接口
                    pNetDevHook->open = main_virtual_if_open;
                RTMP_DRIVER_CHIP_PREPARE(pAd);
                RTMP_DRIVER_OP_MODE_GET(pAd, &OpMode)       //向内核注册网络设备
                rv = RtmpOSNetDevAttach(OpMode, net_dev, &netDevHook);

虚拟接口vap的初始化过程如下:

main_virtual_if_open
    VIRTUAL_IF_INIT
        CMD_RTPRIV_IOCTL_VIRTUAL_INF_INIT
            virtual_if_init_handler
                mt_wifi_open
                    RTMP_DRIVER_MCU_SLEEP_CLEAR(pAd);
                    RTMP_DRIVER_OP_MODE_GET(pAd, &OpMode);      //获取模式,ap or sta
                    load_dev_l1profile(pAd)     //加载文件  #define L1_PROFILE_PATH	"/etc/wireless/mt7916/l1profile.dat.dbdc"
                    RTMPPreReadProfile(pAd);    //读文件,设置参数
                    mt_wifi_init    //芯片和其他初始化
                        rtmp_sys_init(pAd, pHostName)   //初始化RTMP适配器(RTMP_ADAPTER)和相关的系统配置
                            wifi_sys_reset(&pAd->WifiSysInfo)       //重置WiFi系统信息
                            RtmpMgmtTaskInit(pAd)       //初始化管理任务
                            HwCtrlInit(pAd)             //初始化硬件控制
                            rtmp_cfg_init(pAd, pHostName)       //Initialize pAd->StaCfg[], pAd->ApCfg, pAd->CommonCfg to manufacture default
                            MeasureReqTabInit(pAd)              //Initialize pAd->StaCfg[], pAd->ApCfg, pAd->CommonCfg to manufacture default
                            qm_init(pAd)            //初始化队列管理器
                            tm_init(pAd)            //初始化流量管理器
                        WfInit(pAd)
                        MlmeInit(pAd)   //mlme初始化
                            sync_fsm_init           //fsm初始化
                            WpaStateMachineInit(pAd, &pAd->Mlme.WpaMachine, pAd->Mlme.WpaFunc);     //各类状态机初始化
                            RTMPInitTimer(pAd, &pAd->Mlme.PeriodicTimer, GET_TIMER_FUNCTION(MlmePeriodicExecTimer), pAd, TRUE);     //定时器初始化
                        rtmp_ap_init(pAd);  //ap功能初始化
                        mt_sys_ready(pAd);  //软件初始化完成,进行系统准备
                            chip_interrupt_enable(ad);      //芯片接口enable
                            RTMPEnableRxTx(ad);             //Enable RxTx
                    RT28xx_MBSS_Init(pAd, net_dev);     //multi-bss初始化
                    RTMPDrvOpen(pAd);
    VIRTUAL_IF_UP
        CMD_RTPRIV_IOCTL_VIRTUAL_INF_UP
            virtual_if_up_handler
                wdev_do_open
                    wdev->wdev_ops->open(wdev);
                        .open = ap_inf_open,

三、主要代码流程

1.信道切换流程

信道切换主要包含以下几个过程:

  1. 触发信道切换,原信道进行信道切换宣告(csa),原信道发包含csa信息的beacon;
  2. 原信道csa结束,构建新信道beacon,切换至新信道,beacon同步切换至新信道;
  3. 新信道若触发静默(cac),待cac时间结束后,结束静默,新信道beacon正常发出。

代码流程如下:

**触发信道切换,原信道触发csa,涉及MakeBeacon:**
rtmp_set_channel                                        drivers/MT7916/mt_wifi/embedded/common/cmm_info.c
    perform_channel_change
        HcUpdateCsaCntByChannel                                 drivers/MT7916/mt_wifi/embedded/hw_ctrl/hdev_ctrl.c
            UpdateBeaconHandler(pAd, wdev, BCN_UPDATE_CSA);         drivers/MT7916/mt_wifi/embedded/common/bcn.c        (旧信道触发csa count的beacon更新)
                HW_BEACON_UPDATE                                        drivers/MT7916/mt_wifi/embedded/hw_ctrl/hw_ctrl_cmd.c
                    {HWCMD_ID_UPDATE_BEACON, HwCtrlHandleUpdateBeacon, 0},  drivers/MT7916/mt_wifi/embedded/hw_ctrl/hw_ctrl.c
                        HwCtrlHandleUpdateBeacon
                            UpdateBeaconProc                                        drivers/MT7916/mt_wifi/embedded/common/bcn.c
                                MakeBeacon   
                                    pBeaconFrame = (UCHAR *)(tmac_info + tx_hw_hdr_len);        帧内容pBeaconFrame保存在struct wifi_dev *wdev中
                                    FrameLen = ComposeBcnPktHead(pAd, wdev, pBeaconFrame);
                                    ptr = pBeaconFrame + pbcn_buf->cap_ie_pos;
                                    ptr = pBeaconFrame + pbcn_buf->TimIELocationInBeacon;
                                    FrameLen += BcnTimUpdate(pAd, wdev, ptr);
                                    ComposeBcnPktTail(pAd, wdev, &UpdatePos, pBeaconFrame);
                                        MakeChSwitchAnnounceIEandExtend
                                    write_tmac_info_offload_pkt
                                        asic_write_tmac_info_fixed_rate(pAd, tmac_buf, &mac_info, TransmitSet);

**原信道csa结束,新信道beacon更新:**
ChannelSwitchingCountDownProc
    RTEnqueueInternalCmd(pAd, CMDTHRED_DOT11H_SWITCH_CHANNEL, &apIdx, sizeof(UCHAR));
        {CMDTHRED_DOT11H_SWITCH_CHANNEL, Dot11HCntDownTimeoutAction},       //CMDHandler中接收命令cmd
            Dot11HCntDownTimeoutAction
                UpdateBeaconHandler(pAd, tdev, BCN_UPDATE_DISABLE_TX);          //旧信道beacon停止
                ap_phy_rrm_init_byRf(pAd, wdev);                                //更新信道
                    wlan_operate_init
                        radio_operate_init                              //init radio
                            DfsBypassRadarStateCheck(wdev)              //if (pDot11h->RDMode == RD_NORMAL_MODE)    return TRUE;其他情况返回false
                            RadarStateCheck                             //检测雷达状态
                                pDot11h->RDMode = RD_SILENCE_MODE;      //设置静默模式
                            operate_loader_phy(wdev, &fcfg);            //load,进入静默
                    UpdateBeaconHandler(pAd, tdev, BCN_UPDATE_ENABLE_TX);       //更新新信道的beacon

**新信道cac静默时间结束:**
MlmePeriodicExecTimer
    MlmePeriodicExec
        APMlmePeriodicExec
            MlmeEnqueue(pAd, DFS_STATE_MACHINE, DFS_CAC_END, 0, NULL, HcGetBandByWdev(wdev));                   //结束cac
            AsicSetSyncModeAndEnable(pAd, pAd->CommonCfg.BeaconPeriod[DBDC_BAND0], HW_BSSID_0, OPMODE_AP);
            pDot11hTest->RDMode = RD_NORMAL_MODE;

2.数据包收发流程

rx方向:

rt2860_interrupt
    pci_handle_irq
        pci_hif_chip->isr(pci_hif_chip);
            hif->isr = mt7906_isr;
                sched_ops->schedule_rx_data_done(task_group);
                    .schedule_rx_data_done = tasklet_schedule_rx_data_done,
                        RTMP_OS_TASKLET_SCHE(&task_group->rx_data_done_task);
                            pci_rx_data_done_func
                                pci_hif->dma_done_handle[HIF_RX_DATA](pAd, rx_ring->resource_idx)
                                    pci_rx_dma_done_handle
                                        pkt = hif->get_pkt_from_rx_resource[rx_ring->buf_type][rx_ring->get_pkt_method](pAd, &bReschedule, &RxPending, resource_idx);    //读取rxring重建skb,重新填充rxring
                                        asic_rx_pkt_process(pAd, resource_idx, p_rx_blk, pkt);
                                            arch_ops->rx_pkt_process(pAd, resource_idx, pRxBlk, pRxPacket);
                                                mt_rx_pkt_process
                                                    rx_data_handler(pAd, rx_blk, rx_packet);
                                                        asic_trans_rxd_into_rxblk(pAd, rx_blk, rx_packet)
                                                            arch_ops->trans_rxd_into_rxblk(pAd, rx_blk, rx_pkt);
                                                                mtf_trans_rxd_into_rxblk                            //根据收到的内容,将接收到的数据包(rx_pkt)转换为适配器结构体(pAd)中的一个接收块(rx_blk),填充无线帧信息 地址等和报文信息
                                                        rx_packet_process(pAd, rx_packet, rx_blk);
                                                            dev_rx_802_3_data_frm(pAd, pRxBlk);
                                                                ops->ieee_802_3_data_rx(pAd, wdev, pRxBlk, pEntry)
                                                                    ap_ieee_802_11_data_rx
                                                                        rx_802_3_data_frm_announce(pAd, pEntry, pRxBlk, pEntry->wdev);
                                                                            indicate_802_3_pkt(pAd, pRxBlk, wdev->wdev_idx);
                                                                                announce_or_forward_802_3_pkt
                                                                                    to_os = ops->rx_pkt_foward(pAd, wdev, pPacket);
                                                                                        .rx_pkt_foward = ap_rx_pkt_foward,
                                                                                            ap_rx_pkt_foward                            //是否进行空口转发,并返回是否继续处理还是丢弃
                                                                                                if (to_air) send_data_pkt(pAd, wdev, pForwardPacket);   //to_air, 空口转发
                                                                                    if (to_os == TRUE)  announce_802_3_packet(pAd, pPacket, op_mode);   //to_os == TRUE, 继续处理
                                                                                        RtmpOsPktRcvHandle(pRxPkt, napi);               //to os
                                                                                            wlan_rx_handle(skb, wdev->node, skipidm)    //走idm
                                                                                                if (0 == skipidm) wlan_cspkernel_ops.idm_fdb_recv_handle(node, skb)   //idm接收
                                                                                                    idm_fdb_recv_handle     //此函数返回0时,表示报文处理完毕,上层不用再处理此报文;返回非0值,上层还需要继续处理送到加速接口或协议栈
                                                                                                        skb = idm_vlan_upstream_handle(node, skb);                                  //打上vlan标签
                                                                                                        if (Double_RateLmt_Handle(skb, SKB_OUTDEVRL_IN) == QOS_POLICER_ACT_DROP)    //根据QoS策略决定是否丢弃数据包
                                                                                                        return node->idm_dev->netdev_ops->ndo_start_xmit(skb, node->idm_dev);       //发送数据包至idm
                                                                                                            static int idm_net_tx(struct sk_buff *skb_list, struct net_device *dev)
                                                                                                                net_idm_tx
                                                                                                                    idm_tx
                                                                                                                        idm_wifi_tx
                                                                                            ffe_receive_skb_locked(skb, ETH_P_ALL)      //走软加速,给转发模块处理
                                                                                            netif_rx(skb);                              //上协议栈

tx方向:

idm_fdb_forward
    idm_vlan_downstream_ucast_handle                          //去除vlan标签
        ndev->netdev_ops->ndo_start_xmit(skb, ndev)                         //发送数据包(至wlan驱动)
            pNetDevOps->ndo_start_xmit = (HARD_START_XMIT_FUNC) (pDevOpHook->xmit);
                pNetDevHook->xmit = rt28xx_send_packets;
                    rt28xx_packet_xmit(skb)     //到mtk驱动
                        RTMPSendPackets((NDIS_HANDLE)wdev, (PPNDIS_PACKET) & pPacket, 1, skb->len, RtmpNetEthConvertDevSearch);
                            wdev_tx_pkts((NDIS_HANDLE)pAd, (PPNDIS_PACKET) & pPacket, 1, wdev)
                                send_data_pkt(pAd, wdev, pPacket);
                                    ops->fp_send_data_pkt(pAd, wdev, pkt);
                                        fp_send_data_pkt
                                            ops->enq_mgmtq_pkt(pAd, wdev, pkt);
                                            ops->enq_dataq_pkt(pAd, wdev, pkt, q_idx);
                                                fp_enq_dataq_pkt
                                                    tm_ops->schedule_task(pAd, TX_DEQ_TASK, idx);
                                                        .schedule_task = tm_tasklet_qm_schedule_task,
                                                            tm_tasklet_qm_schedule_task
                                                                RTMP_OS_TASKLET_SCHE(&pAd->tx_deque_tasklet[idx])
                                                                    RTMP_OS_TASKLET_INIT(pAd, &pAd->tx_deque_tasklet[0], fp_tx_pkt_deq_tasklet, (unsigned long)pAd)
                                                                        static VOID fp_tx_pkt_deq_tasklet(ULONG param)
                                                                            fp_tx_pkt_deq_func                          //tx pkt出队
                                                                                pkt = fp_get_tx_element(pAd, idx);      //获取pkt内容
                                                                                ret = wdev_ops->tx_pkt_handle(pAd, wdev, tx_blk);
                                                                                    .tx_pkt_handle = ap_tx_pkt_handle,
                                                                                        ret = ops->legacy_tx(pAd, wdev, tx_blk);
                                                                                            ap_legacy_tx
                                                                                                ret = asic_hw_tx(pAd, tx_blk);
                                                                                                    arch_ops->hw_tx(ad, tx_blk);
                                                                                                        mt_ct_hw_tx
                                                                                                            asic_write_tmac_info(pAd, &tx_blk->HeaderBuf[0], tx_blk);
                                                                                                                arch_ops->write_tmac_info(pAd, buf, pTxBlk);
                                                                                                                    mtf_write_tmac_info
                                                                                                            ret = asic_write_txp_info(pAd, &tx_blk->HeaderBuf[cap->tx_hw_hdr_len], tx_blk);
                                                                                                                arch_ops->write_txp_info(pAd, buf, pTxBlk);
                                                                                                                    mtf_write_txp_info_by_wa
                                                                                                                        token_tx_enq                 //获取token入队
                                                                                                            asic_write_tx_resource(pAd, tx_blk, TRUE, &free_cnt);
                                                                                                                arch_ops->write_tx_resource(pAd, pTxBlk, bIsLast, freeCnt);
                                                                                                                    mtd_pci_write_tx_resource
                                                                                hif_kickout_data_tx(pAd, tx_blk, hif_idx);
                                                                                    ops->kickout_data_tx(ad, tx_blk, resource_idx);
                                                                                        ops->kickout_data_tx = pci_kick_out_data_tx;
                                                                                            HIF_IO_WRITE32(pAd->hdev_ctrl, tx_ring->hw_cidx_addr, tx_ring->TxCpuIdx);

四、应用层和驱动的通信方式

驱动作为内核的一部分,需要和应用层进行通信,通信方式以ioctl为主,以下给出核心代码流程:

1.set指令发送

应用层调用set指令进行信道配置:

__priv_wlan_ioctl_set(0, "channel=1")

INT32 __priv_wlan_ioctl_set(INT32 iIfIndex, const CHAR *szCmd)
{
    INT32  iSockedfd                = INVALID_SOCKET;
    CHAR   szIfName[WL_SM_SIZE_MAX] = {0};
    CHAR   szData[WL_XXXXL_SIZE_MAX] = {0};
    struct iwreq wrq;
    memset(&wrq, 0, sizeof(wrq));

    iSockedfd = socket(AF_INET, SOCK_DGRAM, 0);         //新建socket
    if (iSockedfd < 0)
    {
        return FALSE;
    }

    snprintf(szIfName, sizeof(szIfName), "%s", WLANIfName[iIfIndex]);   //从全局变量中更根据iIfIndex获取szIfName
    strncpy(wrq.ifr_name, szIfName, sizeof(wrq.ifr_name) - 1);      //构建wrq结构体
    strncpy(szData, szCmd, sizeof(szData) - 1);
    wrq.u.data.pointer = szData;
    wrq.u.data.length  = strlen(szData);
    wrq.u.data.flags   = 0;

    if (ioctl(iSockedfd, RTPRIV_IOCTL_SET, &wrq) < 0)       //调用ioctl
    {
        close(iSockedfd);
        return FALSE;
    }

    close(iSockedfd);

    return TRUE;
}

2.get指令发送

应用层调用get指令获取接口参数:

priv_wlan_get_stats(IfName, &Wlan_stats);

static INT32 priv_wlan_get_stats(CHAR *szIfName, T_WLAN_STATS *ptWlanStats)
{
    INT32              iSockedfd    = INVALID_SOCKET;
    PMBSS_STATISTICS   ptBufferTmp  = NULL;
    struct iwreq wrq;

    ptBufferTmp = (PMBSS_STATISTICS)malloc(sizeof(MBSS_STATISTICS));
    memset(ptBufferTmp, 0, sizeof(MBSS_STATISTICS));

    iSockedfd = socket(AF_INET, SOCK_DGRAM, 0);              //新建socket
    if (iSockedfd < 0)
    {
        free(ptBufferTmp);
        ptBufferTmp = NULL;
        return FALSE;
    }

    memset(&wrq, 0, sizeof(wrq));
    strncpy(wrq.ifr_name, szIfName, sizeof(wrq.ifr_name) - 1);      //构建请求结构体
    wrq.u.data.pointer = ptBufferTmp;
    wrq.u.data.length  = sizeof(MBSS_STATISTICS);
    wrq.u.data.flags   = 0;

    if (ioctl(iSockedfd, RTPRIV_IOCTL_GPACKETSTAT, &wrq) < 0)       //调用ioctl
    {
        free(ptBufferTmp);
        ptBufferTmp = NULL;
        close(iSockedfd);
        return FALSE;
    }
    close(iSockedfd);

    memset(ptWlanStats, 0, sizeof(T_WLAN_STATS));                   //将获取结果的ptBufferTmp赋值给ptWlanStats
    ptWlanStats->ullTotalBytesSent               = ptBufferTmp->TransmittedByteCount;
    ptWlanStats->ullTotalBytesReceived           = ptBufferTmp->ReceivedByteCount;
    ptWlanStats->ullTotalPacketsSent             = ptBufferTmp->TxCount;
    ptWlanStats->ullTotalPacketsReceived         = ptBufferTmp->RxCount;
    ptWlanStats->ullErrorsSent                   = ptBufferTmp->TxErrorCount;
    ptWlanStats->ullErrorsReceived               = ptBufferTmp->RxErrorCount;
    ptWlanStats->ullDiscardPacketsSent           = ptBufferTmp->TxDropCount;
    ptWlanStats->ullDiscardPacketsReceived       = ptBufferTmp->RxDropCount;

    free(ptBufferTmp);
    ptBufferTmp = NULL;
    return MGR_OK;
}

3.接收处理

驱动接收指令并处理:

pNetDevHook->ioctl = rt28xx_ioctl;      //RtmpPhyNetDevInit中初始化
    rt28xx_ioctl
        ret = ops->ioctl(net_dev, rq, cmd);
            .ioctl = rt28xx_ap_ioctl,
                rt28xx_ap_ioctl             //在rt28xx_ap_ioctl中根据cmd进行不同操作
                    case RTPRIV_IOCTL_SET:      //set操作,串口可通过iwpriv wlanX set channel=1触发
                    Status = RTMP_AP_IoctlHandle(pAd, wrq, CMD_RTPRIV_IOCTL_SET, 0, NULL, 0);
                        case CMD_RTPRIV_IOCTL_SET:
                        Status = RTMPAPPrivIoctlSet(pAd, wrq);
                            for (PRTMP_PRIVATE_SET_PROC = RTMP_PRIVATE_SUPPORT_PROC; PRTMP_PRIVATE_SET_PROC->name; PRTMP_PRIVATE_SET_PROC++)    //RTMP_PRIVATE_SUPPORT_PROC保存各个set选项名称和对应处理函数,遍历得到对应函数
                            PRTMP_PRIVATE_SET_PROC->set_proc(pAd, value)        //执行对应处理函数
                                {"Channel",					Set_Channel_Proc},
                                    Set_Channel_Proc
                                        success = rtmp_set_channel(pAd, wdev, Channel);
                    case RTPRIV_IOCTL_GPACKETSTAT:        //定制get操作    
                    RTMP_AP_IoctlHandle(pAd, wrq, CMD_RT_PRIV_IOCTL, RT_OID_802_11_PER_BSS_STATISTICS, NULL, 0);
                        case CMD_RT_PRIV_IOCTL:
                        Status = RTMPAPQueryInformation(pAd, wrq, (INT)subcmd);
                            case RT_OID_802_11_PER_BSS_STATISTICS
                            pMbssStat->TransmittedByteCount = pMbss->TransmittedByteCount;      //从驱动结构体pMbss中获取统计信息存入pMbssStat
                            pMbssStat->ReceivedByteCount =  pMbss->ReceivedByteCount;
                            pMbssStat->TxCount =  pMbss->TxCount;
                            pMbssStat->RxCount =  pMbss->RxCount;
                            Status = copy_to_user(wrq->u.data.pointer, pMbssStat, wrq->u.data.length);  //pMbssStat存入请求结构体中

总结

以上是对mtk7916驱动的初步学习理解,欢迎讨论指点!

参考链接;
内核编译:
https://zhuanlan.zhihu.com/p/623969313
https://cloud.tencent.com/developer/article/1708866
https://zhuanlan.zhihu.com/p/146930526
https://blog.csdn.net/weixin_37787043/article/details/140379610
https://mp.weixin.qq.com/s?__biz=MzI2OTE0ODY5Mw==&mid=2247484283&idx=2&sn=7891e6605a1377d19f12e901215c6e89&chksm=eae5f401dd927d1758a8b8ed0ac237cac544e4daca59f63a5d9b82b1dd672ee92c291be81d16&scene=27

linux无线网络:
https://zhuanlan.zhihu.com/p/650953280
https://zhuanlan.zhihu.com/p/532771729

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值