RK3568-RTL8852BS驱动框架

驱动结构框架
  • LINUX RTL8852BS驱动框架很繁杂
  • 结合Bear+Clangd+Vscode+Ubuntu阅读代码
  • 梳理出下列流程框架
综述
c 复制代码
module_init(rockchip_wifi_init_module_rtkwifi)
-->
//drivers\net\wireless\rockchip_wlan\rtl8852bs\os_dep\linux\sdio_intf.c
rtw_drv_entry
-->
//对比参考文档32 usb_register(&usb_drv->usbdrv) usb注册 
sdio_register_driver(&sdio_drvpriv.rtw_sdio_drv)
-->
static struct sdio_drv_priv sdio_drvpriv 
---> 
.rtw_sdio_drv.probe = rtw_dev_probe
.rtw_sdio_drv.id_table = sdio_ids
--->
rtw_dev_probe() //drivers\net\wireless\rockchip_wlan\rtl8852bs\os_dep\linux\sdio_intf.c

|---> sdio_dvobj_init() //初始化dvobj
	|---> devobj_init() //初始化dvobj_priv结构体,并将返回值赋给dvobj
	|---> sdio_set_drvdata() //将dvobj赋值给func的drvdata成员
	|---> dvobj_to_sdio //将dvobj转换为sdio_data结构体,并将返回值赋给psdio
	//对比参考文档32 usb_dvobj_init
	|---> rtw_sdio_init() // 初始化dvobj
		|---> sdio_claim_host()//获取SDIO功能
		|---> sdio_enable_func()//申请SDIO主机
		|---> sdio_set_block_size() //设置块大小
	|--->dvobj->intf_ops = &sdio_ops //drivers\net\wireless\rockchip_wlan\rtl8852bs\core\rtw_trx_sdio.c
		.read      = rtw_sdio_raw_read
//drivers\net\wireless\rockchip_wlan\rtl8822bs\os_dep\linux\sdio_ops_linux.c
	|---> rtw_reset_continual_io_error()// 将sdio_ops赋值给dvobj的intf_ops

|---> devobj_trx_resource_init()  //  初始化dvobj的传输资源
	|---> rtw_init_lite_xmit_resource() // 初始化轻量级发送资源
	|---> rtw_init_lite_recv_resource() //初始化轻量级接收资源
	|---> rtw_init_recv_priv() //初始化接收私有数据结构
	|---> rtw_init_cmd_priv() //初始化命令私有数据结构

|---> rtw_hw_init() //该函数负责初始化硬件设备 drivers\net\wireless\rockchip_wlan\rtl8852bs\core\rtw_phl.c
	|---> rtw_phl_init() //  初始化phl
		|---> phl_regulation_init() //  初始化phl_regulation
		|---> phl_com_init() //  初始化phl_com
		|---> phl_hci_init() //  初始化phl_hci
		|---> phl_set_hci_ops() //  设置phl_hci操作、
			|--->phl_hook_trx_ops_sdio
			|--->_phl_hci_ops_check
				|--->phl_trx_init_sdio
					|--->phl_register_trx_hdlr_sdio
						|--->phl_register_handler
					|--->phl_tx_sdio_thrd_hdl//核心线程
						|--->rtw_hal_sdio_tx
		|---> phl_fsm_init()  phl_fsm_module_init(phl_info)//  初始化fsm状态
		|---> phl_twt_init()  //  初始化TWT状态
		|---> rtw_hal_init() //  初始化hal状态
			|---> hal_set_ops 
				|--->hal_set_ops_8852bs //设置操作函数
				|--->hal_set_ops_8852b 
					|--->ops->read_macreg
					|--->ops->read_bbreg	
						|--->hal_read32 _hal_read32
							|--->_read32 = io_priv->io_ops._read32
								|--->hal_mac_sdio_read32
									|--->reg_read32
										|--->reg_read32_sdio
											|--->rtw_sdio_raw_write
												|--->dw_mci_queue_request
													|--->__raw_writel
														|--->asm volatile
		|---> rtw_hal_hci_cfg() //  将总线信息发送给hal
		|---> rtw_hal_read_chip_info()//  从mac/bb/rf/btc/efuse/fw-defeature-rpt获取硬件能力
		|---> rtw_hal_var_init() //  初始化hal变量
		|---> phl_var_init()//  初始化phl变量
		|---> phl_mr_ctrl_init() //  初始化mr_ctrl和wifi_role[]
		|---> phl_module_init()//  初始化PHL模块
			|---> phl_msg_hub_init() //  初始化消息中心
			|---> phl_wow_mdl_init() //  初始化WoW模块
			|---> phl_pkt_ofld_init() //  初始化数据包转发模块
			|---> phl_test_module_init() //  初始化phl_test_module模块
			|---> phl_p2pps_init() //  初始化phl_p2pps模块
			|---> phl_disp_eng_init() //  初始化phl_disp_eng模块
			|---> phl_register_background_module_entry()  //  注册phl_disp_eng模块的入口
			|---> phl_ecsa_ctrl_init()//  初始化ECDSA控制结构
		|---> phl_macid_ctrl_init()  //  初始化macid_ctrl和stainfo_ctrl
		|---> phl_stainfo_ctrl_init() //  在hal_init后初始化hal_sta_info


	|---> rtw_core_update_default_setting()
	// 该函数用于更新默认设置,包括加载物理层参数、设置固件能力、控制LED模式以及设置RPQ聚合数
	|---> rtw_phl_cap_pre_config() //软件和硬件能力预配置
	|---> rtw_phl_init_ppdu_sts_para() //初始化PPDU状态参数	
	|---> rtw_phl_trx_alloc() //初始化数据路径部分
	|---> rtw_core_register_phl_msg() //注册PHL消息
	|---> rtw_phl_preload()//对dvobj->phl进行预处理
	|---> rtw_phl_final_cap_decision()//对dvobj->phl进行最终容量决策
	|---> rtw_core_register_mr_config()//注册mr配置
	|---> rtw_core_set_ecsa_ops //设置ecsa操作
	|---> rtw_hw_dump_hal_spec()//输出hal规范
	|---> rtw_phl_watchdog_init()//初始化watchdog
	|---> rtw_set_phl_regulation_ctx() //设置PHL的上下文

|---> rtw_sdio_primary_adapter_init()  //该函数用于初始化SDIO主适配器
	|---> rtw_load_registry() //加载注册表 /*registry_priv*/
		|---> chip_version
		|---> rfintfs
		|---> lbkmode
		|---> network_mode //设置模式, 可能和AP有关
		|---> _rtw_memcpy //SSID
		|---> band
		|---> channel
		|---> wireless_mode
		|---> band_type
		
	|---> rtw_init_drv_sw() //初始化驱动软件
	//drivers\net\wireless\rockchip_wlan\rtl8852bs\os_dep\linux\os_intfs.c
		|---> rtw_init_default_value()
		|---> rtw_init_hal_com_default_value()
		|---> rtw_init_cmd_priv() fail ---> rtw_free_cmd_priv()
		|---> rtw_init_evt_priv() fail ---> rtw_free_evt_priv()
		|---> rtw_init_mlme_priv()
		|---> init_mlme_ext_priv()  fail ---> free_mlme_ext_priv() rtw_free_mlme_priv()
		|---> _rtw_init_xmit_priv() fail ---> _rtw_free_xmit_priv()
		|---> _rtw_init_recv_priv() fail ---> _rtw_free_recv_priv()
		|---> _rtw_init_sta_priv()
		|---> rtw_init_bcmc_stainfo()
		|---> rtw_init_pwrctrl_priv()
		|---> rtw_hal_dm_init()
		
	|---> rtw_hw_get_mac_addr() //  获取MAC地址
	|---> rtw_macaddr_cfg() //获取适配器的MAC地址,并将其作为参数传递给rtw_macaddr_cfg函数


|---> rtw_drv_add_vir_ifaces(dvobj) //如果配置了并发模式,添加虚拟接口
|---> devobj_data_init() //从注册表和芯片规格初始化设备对象(dvobj)的数据
	|---> dev_set_drv_stopped() //将dvobj参数传入,将驱动程序设置为停止状态	
	|---> dev_clr_hw_start() //传入dvobj参数
	|---> devobj_set_phl_regulation_capability() //设置PHL调节能力
	|---> devobj_decide_init_chplan()  //  初始化设备对象的数据计划
	|---> rtw_rfctl_init //  初始化设备对象的射频控制
	|---> rtw_edcca_mode_update()//  更新设备对象的EDCCA模式
	|---> rtw_update_phl_edcca_mode() //  更新PHL的EDCCA模式
	|---> rtw_rfctl_chplan_init()//  初始化设备对象的信道计划
	|---> rtw_hw_cap_init()//  初始化设备对象的硬件能力
	|---> RTW_ENABLE_FUNC(dvobj, DF_RX_BIT) //  启用设备对象的接收功能
	|---> RTW_ENABLE_FUNC(dvobj, DF_TX_BIT) //  启用设备对象的发送功能

|---> rtw_os_ndevs_init() // 分配网络设备名称 注册网络设备
|---> rtw_sdio_alloc_irq() // 分配SDIO中断
c 复制代码
rtw_init_netdev
	rtw_hook_if_ops(pnetdev)
		rtw_netdev_ops
	pnetdev->wireless_handlers = (struct iw_handler_def *)&rtw_handlers_def;
  • rtw_netdev_ops 就是 Realtek Wi-Fi 驱动向内核注册的"网卡操作表",内核通过这些回调函数把用户态或协议栈的请求(打开网卡、发送数据、设置 MAC 等)转发给驱动里对应的处理函数
c 复制代码
static const struct net_device_ops rtw_netdev_ops = {
	.ndo_init = rtw_ndev_init,
	.ndo_uninit = rtw_ndev_uninit,
	.ndo_open = netdev_open,
	.ndo_stop = netdev_close,
	.ndo_start_xmit = rtw_xmit_entry,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
	.ndo_select_queue	= rtw_select_queue,
#endif
	.ndo_set_mac_address = rtw_net_set_mac_address,
	.ndo_get_stats = rtw_net_get_stats,
	.ndo_do_ioctl = rtw_ioctl,
};
字段名 作用 驱动中的对应函数
.ndo_init 网络设备被注册 时调用一次,做一次性初始化 rtw_ndev_init
.ndo_uninit 网络设备被注销 时调用,做清理 rtw_ndev_uninit
.ndo_open 当用户执行 ifconfig wlan0 upip link set wlan0 up 时调用 netdev_open
.ndo_stop 当用户执行 ifconfig wlan0 downip link set wlan0 down 时调用 netdev_close
.ndo_start_xmit 核心发送函数,协议栈每次要发包时都会调用 rtw_xmit_entry
.ndo_select_queue(内核 ≥ 2.6.35) 多队列网卡选择哪个发送队列 rtw_select_queue
.ndo_set_mac_address 用户通过 ifconfig wlan0 hw ether xx:xx:xx:xx:xx:xx 设置 MAC 地址时调用 rtw_net_set_mac_address
.ndo_get_stats 用户通过 ifconfig wlan0 查看网卡统计信息时调用 rtw_net_get_stats
.ndo_do_ioctl 用户通过 ioctl() 调用私有命令时调用
c 复制代码
rtw_xmit_entry
	core_tx_call_phl
		rtw_phl_add_tx_req
		rtw_phl_tx_req_notify
			phl_schedule_handler
  • phl_tx_sdio_thrd_hdl核心线程
c 复制代码
rtw_phl_init
	phl_set_hci_ops
		phl_hook_trx_ops_sdio
		_phl_hci_ops_check
			phl_trx_init_sdio
				phl_register_trx_hdlr_sdio
					phl_register_handler
						phl_tx_callback_sdio
							phl_handle_xmit_ring_sdio
						phl_rx_callback_sdio
			_os_thread_init(drv, &hci->tx_thrd, phl_tx_sdio_thrd_hdl,phl_info, "rtw_sdio_tx")
  • 事实上也是调用rtw_sdio_raw_write
c 复制代码
phl_tx_sdio_thrd_hdl
	rtw_hal_sdio_tx
		rtw_sdio_write_cmd53
			sdio_io
				d->intf_ops->write
				rtw_sdio_raw_write
  • 把 Realtek 驱动自定义的"标准 + 私有 ioctl 处理表"挂接到网卡设备
  • 用户空间执行 iwconfig wlan0 essid "xxx" 时,内核会调用 rtw_handlers_def.standard[] 里对应的函数
  • 用户空间执行 iwpriv wlan0 set_mib txpower=100 时,内核会调用 rtw_handlers_def.private[] 里对应的函数
c 复制代码
iw_handler_def rtw_handlers_def
	iw_handler rtw_handlers
	iw_handler rtw_private_handler
  • iwpriv扩展命令会调用
c 复制代码
rtw_handlers_def
	rtw_private_handler
		rtw_wx_read32
			rtw_phl_read32
				rtw_hal_read32
					hal_read32
						_hal_read32

Part A
  • drivers/net/wireless/rockchip_wlan/rtl8852bs/os_dep/linux/sdio_intf.c
C 复制代码
module_init(rockchip_wifi_init_module_rtkwifi)
	rtw_drv_entry
		sdio_register_driver(&sdio_drvpriv.rtw_sdio_drv)
  • 驱动程序加载时,内核会使用 sdio_register_driver 函数来注册 SDIO 设备驱动程序,并使用 sdio_device_id 结构体数组 sdio_ids 来匹配连接的 SDIO 设备
c 复制代码
static struct sdio_drv_priv sdio_drvpriv = {
	.rtw_sdio_drv.probe = rtw_dev_probe,
	.rtw_sdio_drv.remove = rtw_dev_remove,
	.rtw_sdio_drv.name = (char *)DRV_NAME,
	.rtw_sdio_drv.id_table = sdio_ids,
	.rtw_sdio_drv.drv = {
#ifdef CONFIG_SDIO_HOOK_DEV_SHUTDOWN
		.shutdown = rtw_dev_shutdown,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29))
		.pm = &rtw_sdio_pm_ops,
#endif
	}
};

sdio_ids 匹配钥匙

  • sdio_ids 数组定义了几个不同的 SDIO 设备 ID,包括 RTL8852A 和 RTL8852B。每个条目都指定了设备制造商 ID(0x024c)和产品 ID(0x8852、0xa852、0xb852),以及设备类别(SDIO_CLASS_WLAN)和驱动程序数据(RTL8852A 或 RTL8852B)

  • 当驱动程序加载时,内核会使用这个表来匹配连接的 SDIO 设备。如果找到匹配的条目,内核会调用驱动程序的 rtw_dev_probe 函数来初始化设备

c 复制代码
static const struct sdio_device_id sdio_ids[] = {

#ifdef CONFIG_RTL8852A
	{SDIO_DEVICE(0x024c, 0x8852), .class = SDIO_CLASS_WLAN, .driver_data = RTL8852A},
	{SDIO_DEVICE(0x024c, 0xa852), .class = SDIO_CLASS_WLAN, .driver_data = RTL8852A},
#endif

#ifdef CONFIG_RTL8852B
	{SDIO_DEVICE(0x024c, 0xb852), .class = SDIO_CLASS_WLAN, .driver_data = RTL8852B},
#endif

#if defined(RTW_ENABLE_WIFI_CONTROL_FUNC) /* temporarily add this to accept all sdio wlan id */
	{ SDIO_DEVICE_CLASS(SDIO_CLASS_WLAN) },
#endif
	{ /* end: all zeroes */				},
};

MODULE_DEVICE_TABLE(sdio, sdio_ids);

Part B
  • 注册sdio_ops 处理函数
c 复制代码
rtw_dev_probe
	sdio_dvobj_init
		rtw_sdio_init
			sdio_set_block_size(func, 512)
		dvobj->intf_ops = &sdio_ops
	rtw_hw_init
c 复制代码
struct rtw_intf_ops sdio_ops = { //  定义一个结构体 rtw_intf_ops,用于存储 SDIO 操作函数
	.read		= rtw_sdio_raw_read, //  读取函数
	.write		= rtw_sdio_raw_write, //  写入函数

	/****************** data path *****************/

	/****************** xmit *********************/
	.init_xmit_priv = sdio_init_xmit_priv, //  初始化发送私有数据
	.free_xmit_priv = sdio_free_xmit_priv, //  释放发送私有数据
	.data_xmit	= sdio_data_xmit, //  数据发送函数
	.xmitframe_enqueue = sdio_xmitframe_enqueue, //  发送帧入队函数
	.start_xmit_frame_thread	= sdio_start_xmit_frame_thread, //  启动发送帧线程函数
	.cancel_xmit_frame_thread	= sdio_cancel_xmit_frame_thread, //  取消发送帧线程函数
	#if 0 /*def CONFIG_XMIT_THREAD_MODE*/ //  #if 0 /*def CONFIG_XMIT_THREAD_MODE*/	 发送缓冲区处理函数	 #endif
	.xmit_buf_handler	= sdio_xmit_buf_handler,
	#endif

	/******************  recv *********************/
	.init_recv_priv = sdio_init_recv_priv, //  初始化接收私有数据
	.free_recv_priv = sdio_free_recv_priv, //  释放接收私有数据
	#ifdef CONFIG_RECV_THREAD_MODE
	.recv_hdl = sdio_recv_hdl,
	#endif
};
  • hal_set_ops_sdio
c 复制代码
rtw_dev_probe
	sdio_dvobj_init
	rtw_hw_init
		rtw_phl_init
			rtw_hal_init
				set_intf_ops = hal_sdio_set_io_ops
				hal_init_io_priv(hal_info->hal_com, set_intf_ops)
				hal_set_ops(phl_com, hal_info)
					CONFIG_SDIO_HCI
					hal_set_ops_sdio(phl_com, hal_info)
						CONFIG_RTL8852B
						hal_set_ops_8852bs
  • hal_set_ops_8852bs
c 复制代码
void hal_set_ops_8852bs(struct rtw_phl_com_t *phl_com,
					struct hal_info_t *hal)
{
	struct hal_ops_t *ops = hal_get_ops(hal);

	hal_set_ops_8852b(phl_com, hal);

	ops->init_hal_spec = init_hal_spec_8852bs;
	ops->hal_get_efuse = hal_get_efuse_8852bs;
	ops->hal_init = hal_init_8852bs;
	ops->hal_deinit = hal_deinit_8852bs;
	ops->hal_start = hal_start_8852bs;
	ops->hal_stop = hal_stop_8852bs;
#ifdef CONFIG_WOWLAN
	ops->hal_wow_init = hal_wow_init_8852bs;
	ops->hal_wow_deinit = hal_wow_deinit_8852bs;
#endif /* CONFIG_WOWLAN */
	ops->hal_hci_configure = hal_hci_cfg_8852bs;
	ops->init_default_value = init_default_value_8852bs;

	ops->hal_mp_init = hal_mp_init_8852bs;
	ops->hal_mp_deinit = hal_mp_deinit_8852bs;

	ops->enable_interrupt = hal_enable_int_8852bs;
	ops->disable_interrupt = hal_disable_int_8852bs;
	ops->config_interrupt = hal_config_int_8852bs;
	ops->recognize_interrupt = hal_recognize_int_8852bs;
	ops->recognize_halt_c2h_interrupt = hal_recognize_halt_c2h_int_8852bs;
	ops->clear_interrupt = hal_clear_interrupt_8852bs;
	ops->interrupt_handler = hal_int_hdler_8852bs;
	ops->restore_interrupt = hal_enable_int_8852bs;
}
c 复制代码
rtw_dev_probe
	rtw_hw_init
		hal_set_ops_8852b
c 复制代码
void hal_set_ops_8852b(struct rtw_phl_com_t *phl_com,
					struct hal_info_t *hal)
{
	struct hal_ops_t *ops = hal_get_ops(hal);

	/*** initialize section ***/
	ops->read_chip_version = read_chip_version_8852b;
	ops->hal_cfg_fw = hal_cfg_fw_8852b;

	ops->read_macreg = hal_read_macreg;
	ops->write_macreg = hal_write_macreg;
	ops->read_bbreg = hal_read_bbreg;
	ops->write_bbreg = hal_write_bbreg;
	ops->read_rfreg = hal_read_rfreg;
	ops->write_rfreg = hal_write_rfreg;

#ifdef RTW_PHL_BCN
	ops->cfg_bcn = hal_config_beacon_8852b;
	ops->upt_bcn = hal_update_beacon_8852b;
#endif

	ops->pkt_ofld = rtw_hal_mac_pkt_ofld;
	ops->pkt_update_ids = rtw_hal_mac_pkt_update_ids;
}
c 复制代码
hal_set_ops_8852b
    ops->read_bbreg = hal_read_bbreg;
	    _hal_read32
			io_priv->io_ops._read32
			rtw_hal_com_t *hal->hal_io_priv iopriv-> hal_io_ops io_ops
				u32(*_read32)(struct rtw_hal_com_t *hal, u32 addr)

    ops->write_bbreg = hal_write_bbreg;
	    _hal_write32
		    io_priv->io_ops._write32
		    rtw_hal_com_t *hal->hal_io_priv iopriv-> hal_io_ops io_ops
			    int (*_write32)(struct rtw_hal_com_t *hal, u32 addr, u32 val);
		    

io_ops 哪里注册了呢

  • hal_init_io_priv 起到调用 set_intf_ops作用
  • 其实就是将 hal_mac_sdio_read32 hal_mac_sdio_write32赋值给 rtw_hal_com_t
c 复制代码
rtw_dev_probe
	sdio_dvobj_init
	rtw_hw_init
		rtw_phl_init
			rtw_hal_init
				set_intf_ops = hal_sdio_set_io_ops
					pops->_read32 = hal_mac_sdio_read32
					pops->_write32 = hal_mac_sdio_write32
				hal_init_io_priv(hal_info->hal_com, set_intf_ops)
				hal_set_ops

Part C
  • reg_read32哪里来的?
C 复制代码
hal_mac_sdio_read32(struct rtw_hal_com_t *hal, u32 addr)
	struct rtw_hal_com_t *hal
		hal_info_t *hal_info = hal->hal_priv
		mac_ax_adapter *mac = hal_to_mac(hal_info)
		mac_ax_ops *mac_api = mac->ops
		mac_ax_intf_ops *mac_intf_ops = mac_api->intf_ops
		mac_intf_ops->reg_read32(mac, addr)			
	
  • mac_intf_ops 是 mac_ax_intf_ops类,mac_ax_intf_ops哪里定义?
c 复制代码
rtw_dev_probe
	sdio_dvobj_init
	rtw_hw_init
		rtw_phl_init
			rtw_hal_init
				set_intf_ops = hal_sdio_set_io_ops
				hal_init_io_priv(hal_info->hal_com, set_intf_ops)
				hal_set_ops
				rtw_hal_mac_init
					rtw_plt_cb_init
						rtw_plt_cb.sdio_cmd52_r8 = hal_mac_sdio_cmd52_r8;
					    rtw_plt_cb.sdio_cmd53_r8 = hal_mac_sdio_cmd53_r8;
					mac_ax_ops_init
						get_mac_ax_adapter
							get_mac_8852b_adapter
								adapter->ops->intf_ops = mac_ax_intf_ops &mac8852b_sdio_ops
  • mac8852b_sdio_ops为定义
c 复制代码
static struct mac_ax_intf_ops mac8852b_sdio_ops = {
	reg_read8_sdio, /* reg_read8 */
	reg_write8_sdio, /* reg_write8 */
	reg_read16_sdio, /* reg_read16 */
	reg_write16_sdio, /* reg_write16 */
	reg_read32_sdio, /* reg_read32 */
	reg_write32_sdio, /* reg_write32 */
	tx_allow_sdio, /* tx_allow_sdio */
	tx_cmd_addr_sdio, /* tx_cmd_addr_sdio */
	sdio_pre_init, /* intf_pre_init */
	sdio_init, /* intf_init */
	sdio_deinit, /* intf_init */
	reg_read_n_sdio, /* reg_read_n_sdio */
	NULL, /*get_bulkout_id*/
	NULL, /* ltr_set_pcie */
	NULL, /*u2u3_switch*/
	NULL, /*get_usb_mode*/
	NULL, /*get_usb_support_ability*/
	NULL, /*usb_tx_agg_cfg*/
	NULL, /*usb_rx_agg_cfg*/
	set_sdio_wowlan, /*set_wowlan*/
	NULL, /*ctrl_txdma_ch*/
	NULL, /*clr_idx_all*/
	NULL, /*poll_txdma_ch_idle*/
	NULL, /*poll_rxdma_ch_idle*/
	NULL, /*ctrl_txhci*/
	NULL, /*ctrl_rxhci*/
	NULL, /*ctrl_dma_io*/
	NULL, /* get_io_stat */
	sdio_get_txagg_num, /*get_txagg_num*/
	NULL, /*get_usb_rx_state*/
	sdio_autok_counter_avg, /* pcie_autok_counter_avg */
	dbcc_hci_ctrl_sdio, /* dbcc_hci_ctrl */
};
#endif
  • mac_intf_ops->reg_read32(mac, addr) 即为 reg_read32_sdio(mac, addr)
c 复制代码
u32 reg_read32_sdio(struct mac_ax_adapter *adapter, u32 adr)
{
	u8 pwr_state, reg_domain;
	union {
		__le32 dword;
		u8 byte[4];
	} value32 = { 0x00000000 };

	pwr_state = pwr_state_chk_sdio(adapter);
	reg_domain = reg_chk_sdio(adapter, adr);

	if ((adr & (4 - 1)) == 0) {
		if (pwr_state == SDIO_PWR_OFF && reg_domain == SDIO_REG_LOCAL) {
			value32.byte[0] = PLTFM_SDIO_CMD52_R8(adr);
			value32.byte[1] = PLTFM_SDIO_CMD52_R8(adr + 1);
			value32.byte[2] = PLTFM_SDIO_CMD52_R8(adr + 2);
			value32.byte[3] = PLTFM_SDIO_CMD52_R8(adr + 3);
			return le32_to_cpu(value32.dword);
		}
		if (pwr_state == SDIO_PWR_ON &&
		    (reg_domain == SDIO_REG_LOCAL ||
		    reg_domain == SDIO_REG_WLAN_REG))
			return _pltfm_sdio_cmd53_r32(adapter, adr);
		return r_indir_sdio(adapter, adr, SDIO_IO_DWORD);
	}

	if (reg_domain == SDIO_REG_LOCAL) {
		value32.byte[0] = PLTFM_SDIO_CMD52_R8(adr);
		value32.byte[1] = PLTFM_SDIO_CMD52_R8(adr + 1);
		value32.byte[2] = PLTFM_SDIO_CMD52_R8(adr + 2);
		value32.byte[3] = PLTFM_SDIO_CMD52_R8(adr + 3);
		return le32_to_cpu(value32.dword);
	}
	value32.byte[0] = (u8)r_indir_sdio(adapter, adr, SDIO_IO_BYTE);
	value32.byte[1] = (u8)r_indir_sdio(adapter, adr + 1, SDIO_IO_BYTE);
	value32.byte[2] = (u8)r_indir_sdio(adapter, adr + 2, SDIO_IO_BYTE);
	value32.byte[3] = (u8)r_indir_sdio(adapter, adr + 3, SDIO_IO_BYTE);
	return le32_to_cpu(value32.dword);
}
  • reg_read32_sdio 用到 PLTFM_SDIO_CMD52_R8
  • PLTFM_SDIO_CMD52_R8 调用 hal_mac_sdio_cmd52_r8
  • 本质调用了sdio_ops->write 也就是rtw_sdio_raw_write
c 复制代码
PLTFM_SDIO_CMD52_R8
	sdio_cmd52_r8
		hal_mac_sdio_cmd52_r8
			_os_sdio_cmd52_r8
				rtw_sdio_read_cmd52
					sdio_io
						d->intf_ops->write
						#dvobj->intf_ops = &sdio_ops
							rtw_sdio_raw_write 
			
  • rtw_plt_cb.sdio_cmd52_r8 在 rtw_plt_cb_init定义
c 复制代码
void rtw_plt_cb_init(void)
{
	/* R/W register */
#ifdef CONFIG_SDIO_HCI
	rtw_plt_cb.sdio_cmd52_r8 = hal_mac_sdio_cmd52_r8;
	rtw_plt_cb.sdio_cmd53_r8 = hal_mac_sdio_cmd53_r8;
	rtw_plt_cb.sdio_cmd53_r16 = hal_mac_sdio_cmd53_r16;
	rtw_plt_cb.sdio_cmd53_r32 = hal_mac_sdio_cmd53_r32;
	rtw_plt_cb.sdio_cmd53_rn = hal_mac_sdio_cmd53_rn;
	rtw_plt_cb.sdio_cmd52_w8 = hal_mac_sdio_cmd52_w8;
	rtw_plt_cb.sdio_cmd53_w8 = hal_mac_sdio_cmd53_w8;
	rtw_plt_cb.sdio_cmd53_w16 = hal_mac_sdio_cmd53_w16;
	rtw_plt_cb.sdio_cmd53_w32 = hal_mac_sdio_cmd53_w32;
	rtw_plt_cb.sdio_cmd53_wn = hal_mac_sdio_cmd53_wn;
	rtw_plt_cb.sdio_cmd52_cia_r8 = hal_mac_sdio_cmd52_cia_r8;
#endif /* CONFIG_SDIO_HCI */

#if defined(CONFIG_USB_HCI) || defined(CONFIG_PCI_HCI)
	rtw_plt_cb.reg_r8 = hal_mac_reg_r8;
	rtw_plt_cb.reg_r16 = hal_mac_reg_r16;
	rtw_plt_cb.reg_r32 = hal_mac_reg_r32;
	rtw_plt_cb.reg_w8 = hal_mac_reg_w8;
	rtw_plt_cb.reg_w16 = hal_mac_reg_w16;
	rtw_plt_cb.reg_w32 = hal_mac_reg_w32;
#endif /* CONFIG_USB_HCI || CONFIG_PCI_HCI */

	/* Memory allocate */
	rtw_plt_cb.rtl_free = hal_mac_mem_free;
	rtw_plt_cb.rtl_malloc = hal_mac_mem_alloc;
	rtw_plt_cb.rtl_memcpy = hal_mac_mem_cpy;
	rtw_plt_cb.rtl_memset = hal_mac_mem_set;
	rtw_plt_cb.rtl_memcmp = hal_mac_mem_cmp;
	/* Sleep */
	rtw_plt_cb.rtl_delay_us = hal_mac_udelay;
	rtw_plt_cb.rtl_delay_ms = hal_mac_mdelay;

	/* Process Synchronization */
	rtw_plt_cb.rtl_mutex_init = hal_mac_mutex_init;
	rtw_plt_cb.rtl_mutex_deinit = hal_mac_mutex_deinit;
	rtw_plt_cb.rtl_mutex_lock = hal_mac_mutex_lock;
	rtw_plt_cb.rtl_mutex_unlock = hal_mac_mutex_unlock;

	rtw_plt_cb.msg_print = hal_mac_msg_print;
	rtw_plt_cb.event_notify = hal_mac_event_notify;
	rtw_plt_cb.ser_l2_notify = hal_ser_l2_notify;
	rtw_plt_cb.ld_fw_symbol = hal_mac_ld_fw_symbol;
	/*.tx = ;	*/
#if MAC_AX_PHL_H2C
	rtw_plt_cb.tx = hal_pltfm_tx;
	rtw_plt_cb.rtl_query_h2c = hal_query_h2c_pkt;
#endif
#if MAC_AX_FEATURE_DBGCMD
	rtw_plt_cb.rtl_sprintf = hal_mac_sprintf;
	rtw_plt_cb.rtl_strcmp = hal_mac_strcmp;
	rtw_plt_cb.rtl_strsep = hal_mac_strsep;
	rtw_plt_cb.rtl_strlen = hal_mac_strlen;
	rtw_plt_cb.rtl_strcpy = hal_mac_strcpy;
	rtw_plt_cb.rtl_strpbrk = hal_mac_strpbrk;
	rtw_plt_cb.rtl_strtoul = hal_mac_strtoul;
#endif
}
  • 相当绕呀,reg_read32_sdio最终实现
  • 是调用dvobj->intf_ops = &sdio_ops 中的rtw_sdio_raw_write
  • 最终调用dw_mci_ops->request
c 复制代码
rtw_sdio_raw_write
	sdio_f0_writeb & sdio_writeb
		mmc_io_rw_direct
			mmc_io_rw_direct_host
	sdio_writesb & sdio_memcpy_toio
		sdio_io_rw_ext_helper
			mmc_io_rw_extended
				mmc_wait_for_req
					__mmc_start_req
						mmc_start_request
							__mmc_start_request
								host->ops->request(host, mrq)
									
  • host->ops哪来的,哪定义的?
  • 提前公布答案
c 复制代码
host->ops->request(host, mrq)
	mmc_host_ops dw_mci_ops
Part D
  • kernel\arch\arm64\boot\dts\rockchip
c 复制代码
rk3568-atk-evb1-ddr4-v10-linux.dts
	rk3568-atk-evb1-ddr4-v10.dtsi
		rk3568.dtsi
		rk3568-evb.dtsi
	rk3568-linux.dtsi
	rk3568-screen_choose.dtsi
	rk3568-lcds.dtsi
	
  • rk3568-atk-evb1-ddr4-v10.dtsi
c 复制代码
&sdmmc2 {
	max-frequency = <150000000>;
	supports-sdio;
	bus-width = <4>;
	disable-wp;
	cap-sd-highspeed;
	cap-sdio-irq;
	keep-power-in-suspend;
	mmc-pwrseq = <&sdio_pwrseq>;
	non-removable;
	pinctrl-names = "default";
	pinctrl-0 = <&sdmmc2m0_bus4 &sdmmc2m0_cmd &sdmmc2m0_clk>;
	sd-uhs-sdr104;
	status = "okay";
};


&wireless_wlan {
	pinctrl-names = "default";
	pinctrl-0 = <&wifi_host_wake_irq>;
	WIFI,host_wake_irq = <&gpio3 RK_PD4 GPIO_ACTIVE_HIGH>;
	wifi_chip_type = "rtl8852bs";
	//keep_wifi_power_on;
	//clocks = <&rk809 1>;
	//clock-names = "clk_wifi";
};

&wireless_bluetooth {
	compatible = "bluetooth-platdata";
	clocks = <&rk809 1>;
	clock-names = "ext_clock";
	//wifi-bt-power-toggle;
	uart_rts_gpios = <&gpio2 RK_PB1 GPIO_ACTIVE_LOW>;
	pinctrl-names = "default", "rts_gpio";
	pinctrl-0 = <&uart8m0_rtsn>;
	pinctrl-1 = <&uart8_gpios>;
	BT,reset_gpio    = <&gpio3 RK_PA0 GPIO_ACTIVE_HIGH>;
	//BT,power_gpio    = <&gpio3 RK_PA0 GPIO_ACTIVE_HIGH>;
	BT,wake_gpio     = <&gpio3 RK_PA2 GPIO_ACTIVE_HIGH>;
	BT,wake_host_irq = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>;
	status = "okay";
};
  • rk3568-evb.dtsi
c 复制代码
wireless_wlan: wireless-wlan {
	compatible = "wlan-platdata";
	rockchip,grf = <&grf>;
	wifi_chip_type = "ap6398s";
	status = "okay";
};

wireless_bluetooth: wireless-bluetooth {
	compatible = "bluetooth-platdata";
	clocks = <&rk809 1>;
	clock-names = "ext_clock";
	//wifi-bt-power-toggle;
	uart_rts_gpios = <&gpio2 RK_PB1 GPIO_ACTIVE_LOW>;
	pinctrl-names = "default", "rts_gpio";
	pinctrl-0 = <&uart8m0_rtsn>;
	pinctrl-1 = <&uart8_gpios>;
	BT,reset_gpio    = <&gpio3 RK_PA0 GPIO_ACTIVE_HIGH>;
	BT,wake_gpio     = <&gpio3 RK_PA1 GPIO_ACTIVE_HIGH>;
	BT,wake_host_irq = <&gpio3 RK_PA2 GPIO_ACTIVE_HIGH>;
	status = "okay";
};
  • rk3568.dtsi
c 复制代码
sdmmc2: dwmmc@fe000000 {
	compatible = "rockchip,rk3568-dw-mshc",
			 "rockchip,rk3288-dw-mshc";
	reg = <0x0 0xfe000000 0x0 0x4000>;
	interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
	max-frequency = <150000000>;
	clocks = <&cru HCLK_SDMMC2>, <&cru CLK_SDMMC2>,
		 <&cru SCLK_SDMMC2_DRV>, <&cru SCLK_SDMMC2_SAMPLE>;
	clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
	fifo-depth = <0x100>;
	resets = <&cru SRST_SDMMC2>;
	reset-names = "reset";
	status = "disabled";
};

drivers/mmc/host/dw_mmc-rockchip.c

c 复制代码
static const struct dw_mci_drv_data rk3288_drv_data = {
	.caps			= dw_mci_rk3288_dwmmc_caps,
	.num_caps		= ARRAY_SIZE(dw_mci_rk3288_dwmmc_caps),
	.set_ios		= dw_mci_rk3288_set_ios,
	.execute_tuning		= dw_mci_rk3288_execute_tuning,
	.parse_dt		= dw_mci_rk3288_parse_dt,
	.init			= dw_mci_rockchip_init,
};


static const struct of_device_id dw_mci_rockchip_match[] = {
	{ .compatible = "rockchip,rk2928-dw-mshc",
		.data = &rk2928_drv_data },
	{ .compatible = "rockchip,rk3288-dw-mshc",
		.data = &rk3288_drv_data },
	{},
};

MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match);

dw_mci_rockchip_pltfm_driver

c 复制代码
static struct platform_driver dw_mci_rockchip_pltfm_driver = {
	.probe		= dw_mci_rockchip_probe,
	.remove		= dw_mci_rockchip_remove,
	.driver		= {
		.name		= "dwmmc_rockchip",
		.of_match_table	= dw_mci_rockchip_match,
		.pm		= &dw_mci_rockchip_dev_pm_ops,
	},
};
  • 由DTB 朔源 找到 mmc->ops = &dw_mci_ops
c 复制代码
.compatible = "rockchip,rk3288-dw-mshc"
	dw_mci_rockchip_probe
		dw_mci_pltfm_register(pdev, drv_data) //dw_mci_drv_data rk3288_drv_data
			dw_mci_probe
				dw_mci_init_slot
					mmc->ops = &dw_mci_ops
c 复制代码
static const struct mmc_host_ops dw_mci_ops = {
	.request		= dw_mci_request,
	.pre_req		= dw_mci_pre_req,
	.post_req		= dw_mci_post_req,
	.set_ios		= dw_mci_set_ios,
	.set_sdio_status	= dw_mci_set_sdio_status,
	.get_ro			= dw_mci_get_ro,
	.get_cd			= dw_mci_get_cd,
	.hw_reset               = dw_mci_hw_reset,
	.enable_sdio_irq	= dw_mci_enable_sdio_irq,
	.ack_sdio_irq		= dw_mci_ack_sdio_irq,
	.execute_tuning		= dw_mci_execute_tuning,
	.card_busy		= dw_mci_card_busy,
	.start_signal_voltage_switch = dw_mci_switch_voltage,
	.init_card		= dw_mci_init_card,
	.prepare_hs400_tuning	= dw_mci_prepare_hs400_tuning,
};
Part E
  • 结合PartC
  • host->ops->request(host, mrq) 即为 dw_mci_request
c 复制代码
host->ops->request(host, mrq)
	dw_mci_request
		dw_mci_queue_request
			dw_mci_start_request
				__dw_mci_start_request
					mci_writel
						writel_relaxed
							__raw_writel
								asm volatile("str %w0, [%1]" : : "rZ" (val), "r" (addr))

asm volatile("str %w0, [%1]" : : "rZ" (val), "r" (addr));

这是 GCC 内联汇编,真正干活的只有一条指令:

  • str %w0, [%1]:把寄存器 %w0(32 位宽)里的值,写到由 %1 寄存器保存的地址处。
  • "rZ" (val):告诉编译器"把 val 放到一个通用寄存器里",Z 表示允许使用零寄存器(XZR/WZR)做优化。
  • "r" (addr):把 addr 也放到一个通用寄存器里。
  • volatile:告诉编译器"这段汇编有副作用,不能随意重排或删除"。
c 复制代码
#define __raw_writel __raw_writel
static inline void __raw_writel(u32 val, volatile void __iomem *addr)
{
	asm volatile("str %w0, [%1]" : : "rZ" (val), "r" (addr));
}

  • 在probe的时候我们为wlan0的网络节点的接口中赋值了ndo_open和ndo_open函数,这两个接口是在ifconfig wlan0 up和ifconfig wlan0 down调用的。
c 复制代码
if (!dev_is_hw_start(dvobj)) {
//  如果设备未启动,则清除意外移除标志位和驱动停止标志位,并启用接收和发送功能
status = rtw_hw_start(dvobj);//  调用硬件启动函数
rtw_led_control(padapter, LED_CTL_NO_LINK);
napi_enable(&padapter->napi);
rtw_hw_iface_init(padapter)//  如果网络设备未启用,则初始化网络设备
rtw_netif_wake_queue(pnetdev)//  唤醒网络设备队列
netdev_br_init(pnetdev)//
  • drivers\net\wireless\rockchip_wlan\rtl8852bs\os_dep\linux\os_intfs.c
c 复制代码
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29))
static const struct net_device_ops rtw_netdev_ops = {
	.ndo_init = rtw_ndev_init,
	.ndo_uninit = rtw_ndev_uninit,
	.ndo_open = netdev_open,
	.ndo_stop = netdev_close,
	.ndo_start_xmit = rtw_xmit_entry,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
	.ndo_select_queue	= rtw_select_queue,
#endif
	.ndo_set_mac_address = rtw_net_set_mac_address,
	.ndo_get_stats = rtw_net_get_stats,
	.ndo_do_ioctl = rtw_ioctl,
};
#endif
c 复制代码
void rtw_hook_if_ops(struct net_device *ndev)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29))
	ndev->netdev_ops = &rtw_netdev_ops;
#else
	ndev->init = rtw_ndev_init;
	ndev->uninit = rtw_ndev_uninit;
	ndev->open = netdev_open;
	ndev->stop = netdev_close;
	ndev->hard_start_xmit = rtw_xmit_entry;
	ndev->set_mac_address = rtw_net_set_mac_address;
	ndev->get_stats = rtw_net_get_stats;
	ndev->do_ioctl = rtw_ioctl;
#endif
}
c 复制代码
rtw_hook_if_ops(pnetdev)
struct net_device *rtw_init_netdev(_adapter *old_padapter)
int rtw_os_ndev_alloc(_adapter *adapter)
int rtw_os_ndev_init(_adapter *adapter, const char *name)
cfg80211_rtw_add_virtual_intf
.add_virtual_intf = cfg80211_rtw_add_virtual_intf,
.del_virtual_intf = cfg80211_rtw_del_virtual_intf,
cfg80211_ops rtw_cfg80211_ops    
wiphy = wiphy_new(&rtw_cfg80211_ops, sizeof(struct rtw_wiphy_data));
struct wiphy *rtw_wiphy_alloc(_adapter *padapter, struct device *dev)
int rtw_cfg80211_dev_res_alloc(struct dvobj_priv *dvobj)
int rtw_os_ndevs_alloc(struct dvobj_priv *dvobj)
int rtw_os_ndevs_init(struct dvobj_priv *dvobj)
rtw_dev_probe/* dev_alloc_name && register_netdev */
  • sdio_register_driver ,具体在drivers\mmc\core\sdio_bus.c 文件中
  • sdio_bus_probe识别sdio_bus_match,调用驱动匹配项的.probe
c 复制代码
static struct bus_type sdio_bus_type = {
	.name		= "sdio",
	.dev_groups	= sdio_dev_groups,
	.match		= sdio_bus_match,
	.uevent		= sdio_bus_uevent,
	.probe		= sdio_bus_probe,
	.remove		= sdio_bus_remove,
	.pm		= &sdio_bus_pm_ops,
};

static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
	const struct sdio_device_id *id)
{
	if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
		return NULL;
	if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
		return NULL;
	if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
		return NULL;
	return id;
}


static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
	struct sdio_driver *sdrv)
{
	const struct sdio_device_id *ids;

	ids = sdrv->id_table;

	if (ids) {
		while (ids->class || ids->vendor || ids->device) {
			if (sdio_match_one(func, ids))
				return ids;
			ids++;
		}
	}

	return NULL;
}

static int sdio_bus_match(struct device *dev, struct device_driver *drv)
{
	struct sdio_func *func = dev_to_sdio_func(dev);
	struct sdio_driver *sdrv = to_sdio_driver(drv);

	if (sdio_match_device(func, sdrv))
		return 1;

	return 0;
}

/**
 *	sdio_register_driver - register a function driver
 *	@drv: SDIO function driver
 */
int sdio_register_driver(struct sdio_driver *drv)
{
	drv->drv.name = drv->name;
	drv->drv.bus = &sdio_bus_type;
	return driver_register(&drv->drv);
}
EXPORT_SYMBOL_GPL(sdio_register_driver);
相关推荐
CodeDevMaster28 分钟前
Linux中tmux入门使用指南:告别SSH断线烦恼,提升终端工作效率的神器
linux·运维·ssh
Brandon汐1 小时前
在Linux中部署tomcat
linux·运维·tomcat
sniper_fandc1 小时前
VirtualBox虚拟机网卡配置
linux·网络·虚拟机
白鸽梦游指南1 小时前
RHCE模拟测试
linux·运维·服务器
不会吉他的肌肉男不是好的挨踢男1 小时前
Linux生成自签名 SSL 证书(适用于测试或内部使用)
linux·运维·ssl
飘忽不定的bug2 小时前
linux磁盘加密
linux·开源
herderl2 小时前
【无标题】命名管道(Named Pipe)是一种在操作系统中用于**进程间通信(IPC)** 的机制
java·linux·服务器·嵌入式硬件·php
基于python的毕设2 小时前
C语言宏相关操作
linux·c语言·ubuntu
研究是为了理解2 小时前
Linux Shell:Nano 编辑器备忘
linux·运维·编辑器
千反田真的打不过我2 小时前
阿里云服务linux安装单机版
linux·阿里云·云计算