文章目录
前言
近期对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
定义了该宏为m
,Makefile
中基于该宏决定.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文件
PS:
使用-objs将右侧的对象文件追加到左侧的列表中:
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)
使用-$(CONFIG_CSP_WLAN_VLAN),根据是否开宏,选择是否将对象文件追加到左侧的列表中(-y直接添加):
include $(BUILD_DIR)/csp_config/csp_user_config
obj-m := $(MODULE_NAME).o
$(MODULE_NAME)-y += wlan_led.o
$(MODULE_NAME)-$(CONFIG_CSP_WLAN_VLAN) += idm_vlan.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.信道切换流程
信道切换主要包含以下几个过程:
- 触发信道切换,原信道进行信道切换宣告(csa),原信道发包含csa信息的beacon;
- 原信道csa结束,构建新信道beacon,切换至新信道,beacon同步切换至新信道;
- 新信道若触发静默(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静默时间结束:**
**驱动初始化时会启动MLME定时器和各个模块的状态机,MLME定时执行,将DFS_CAC_END事件输入DFS_STATE_MACHINE队列中,触发DFS相关事件**
MlmePeriodicExecTimer
MlmePeriodicExec
APMlmePeriodicExec
MlmeEnqueue(pAd, DFS_STATE_MACHINE, DFS_CAC_END, 0, NULL, HcGetBandByWdev(wdev)); //结束cac
DfsCacEndUpdate
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