rt2860v2源码分析一

2019-07-13 08:18发布

git出源码:
https://github.com/wuqiong/rt2860v2-for-openwrt-mt7620 再rt2860v2中搜索字符串__init, 找到驱动模块的入口函数: os/linux/rbus_main_dev.c:37:int __init rt2880_module_init(VOID); os/linux/pci_main_dev.c:46:static int __init rt2860_init_module(void); os/linux/pci_main_dev.c:276:static INT __init rt2860_init_module(VOID) 查看rbus_main_dev.cpci_main_dev.c文件,可以发现rt2860v2驱动在pci_main_dev.c中进行了pci设备模块初始化,在rbus_main_dev.c 中是网络设备模块初始化。 pci_main_dev.c 中分析rt2860v2驱动程序的pci设备模块初始化: static INT __init rt2860_init_module(VOID) { DBGPRINT(RT_DEBUG_ERROR, ("register %s ", RTMP_DRV_NAME)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) return pci_register_driver(&rt2860_driver); #else return pci_module_init(&rt2860_driver); #endif } /* */ /* Driver module unload function */ /* */ static VOID __exit rt2860_cleanup_module(VOID) { pci_unregister_driver(&rt2860_driver); } module_init(rt2860_init_module); module_exit(rt2860_cleanup_module); __init 中,pci_register_driver 进行了pci设备注册,rt2860v2驱动模块挂载在pci总线上。 /* */ /* Our PCI driver structure */ /* */ static struct pci_driver rt2860_driver = { name: RTMP_DRV_NAME, id_table: rt2860_pci_tbl, probe: rt2860_probe, #if LINUX_VERSION_CODE >= 0x20412 remove: __devexit_p(rt2860_remove_one), #else remove: __devexit(rt2860_remove_one), #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) #ifdef CONFIG_PM suspend: rt2860_suspend, resume: rt2860_resume, #endif #endif }; PCI设备驱动结构,填充结构体
  • name : RTMP_DRV_NAME 就是rt2860
  • id_talbe :pci设备表,指明该驱动适合那些设备
  • probe :检测驱动设备
  • remove:卸载驱动设备
  • suspend:挂起设备使之处于节能状态
  • resume:唤醒处于挂起态的设备
id_table 中,驱动将选择对应的硬件设备,用此id来识别硬件信息: /* */ /* Ralink PCI device table, include all supported chipsets */ /* */ static struct pci_device_id rt2860_pci_tbl[] __devinitdata = { #ifdef RT2860 {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2860_PCI_DEVICE_ID)}, /*RT28602.4G */ {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2860_PCIe_DEVICE_ID)}, {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2760_PCI_DEVICE_ID)}, {PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC2790_PCIe_DEVICE_ID)}, {PCI_DEVICE(VEN_AWT_PCI_VENDOR_ID, VEN_AWT_PCIe_DEVICE_ID)}, {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7708)}, {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7728)}, {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7758)}, {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7727)}, {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7738)}, {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7748)}, {PCI_DEVICE(EDIMAX_PCI_VENDOR_ID, 0x7768)}, #endif /* RT2860 */ {0,} /* terminate list */ }; 分析pci设备的probe函数,即rt2860v2_probe,该函数内容如下: /* */ /* PCI device probe & initialization function */ /* */ static INT __devinit rt2860_probe( IN struct pci_dev *pci_dev, IN const struct pci_device_id *pci_id) { VOID *pAd = NULL; struct net_device *net_dev; PVOID handle; PSTRING print_name; ULONG csr_addr; INT rv = 0; RTMP_OS_NETDEV_OP_HOOK netDevHook; ULONG OpMode; DBGPRINT(RT_DEBUG_TRACE, ("===> rt2860_probe ")); /*PCIDevInit============================================== */ /* wake up and enable device */ if ((rv = pci_enable_device(pci_dev))!= 0)//使能pci设备 { DBGPRINT(RT_DEBUG_ERROR, ("Enable PCI device failed, errno=%d! ", rv)); return rv; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) print_name = pci_name(pci_dev); #else print_name = pci_dev->slot_name; #endif /* LINUX_VERSION_CODE */ if ((rv = pci_request_regions(pci_dev, print_name)) != 0)//申请PCI的resource { DBGPRINT(RT_DEBUG_ERROR, ("Request PCI resource failed, errno=%d! ", rv)); goto err_out; } /* map physical address to virtual address for accessing register */ csr_addr = (unsigned long) ioremap(pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); if (!csr_addr) { DBGPRINT(RT_DEBUG_ERROR, ("ioremap failed for device %s, region 0x%lX @ 0x%lX ", print_name, (ULONG)pci_resource_len(pci_dev, 0), (ULONG)pci_resource_start(pci_dev, 0))); goto err_out_free_res; } else { DBGPRINT(RT_DEBUG_TRACE, ("%s: at 0x%lx, VA 0x%lx, IRQ %d. ", print_name, (ULONG)pci_resource_start(pci_dev, 0), (ULONG)csr_addr, pci_dev->irq)); } /* Set DMA master */ pci_set_master(pci_dev); /*RtmpDevInit============================================== */ /* Allocate RTMP_ADAPTER adapter structure */ /* handle = kmalloc(sizeof(struct os_cookie), GFP_KERNEL); */ os_alloc_mem(NULL, (UCHAR **)&handle, sizeof(struct os_cookie)); if (handle == NULL) { DBGPRINT(RT_DEBUG_ERROR, ("%s(): Allocate memory for os handle failed! ", __FUNCTION__)); goto err_out_iounmap; } memset(handle, 0, sizeof(struct os_cookie)); ((POS_COOKIE)handle)->pci_dev = pci_dev; #ifdef OS_ABL_FUNC_SUPPORT { RTMP_PCI_CONFIG PciConfig; PciConfig.ConfigVendorID = PCI_VENDOR_ID; /* get DRIVER operations */ RTMP_DRV_OPS_FUNCTION(pRtmpDrvOps, NULL, &PciConfig, NULL); } #endif /* OS_ABL_FUNC_SUPPORT */ rv = RTMPAllocAdapterBlock(handle, &pAd); /* we may need the pci_dev for allocate structure of "RTMP_ADAPTER" */ if (rv != NDIS_STATUS_SUCCESS) goto err_out_iounmap; /* Here are the RTMP_ADAPTER structure with pci-bus specific parameters. */ /* pAd->CSRBaseAddress = (PUCHAR)csr_addr; */ RTMP_DRIVER_PCI_CSR_SET(pAd, csr_addr); /* RTMPInitPCIeDevice(pci_dev, pAd); */ RTMP_DRIVER_PCIE_INIT(pAd, pci_dev); /*NetDevInit============================================== */ net_dev = RtmpPhyNetDevInit(pAd, &netDevHook);//网络设备的初始化 if (net_dev == NULL) goto err_out_free_radev; /* Here are the net_device structure with pci-bus specific parameters. */ net_dev->irq = pci_dev->irq; /* Interrupt IRQ number */ net_dev->base_addr = csr_addr; /* Save CSR virtual address and irq to device structure */ pci_set_drvdata(pci_dev, net_dev); /* Set driver data */ #ifdef NATIVE_WPA_SUPPLICANT_SUPPORT /* for supporting Network Manager */ /* Set the sysfs physical device reference for the network logical device * if set prior to registration will cause a symlink during initialization. */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) SET_NETDEV_DEV(net_dev, &(pci_dev->dev)); #endif #endif /* NATIVE_WPA_SUPPLICANT_SUPPORT */ /*All done, it's time to register the net device to linux kernel. */ /* Register this device */ #ifdef RT_CFG80211_SUPPORT { /* pAd->pCfgDev = &(pci_dev->dev); */ /* pAd->CFG80211_Register = CFG80211_Register; */ /* RTMP_DRIVER_CFG80211_INIT(pAd, pci_dev); */ /* In 2.6.32, cfg80211 register must be before register_netdevice(); We can not put the register in rt28xx_open(); Or you will suffer NULL pointer in list_add of cfg80211_netdev_notifier_call(). */ CFG80211_Register(pAd, &(pci_dev->dev), net_dev);//cfg80211提供的无线网络注册接口 } #endif /* RT_CFG80211_SUPPORT */ RTMP_DRIVER_OP_MODE_GET(pAd, &OpMode); rv = RtmpOSNetDevAttach(OpMode, net_dev, &netDevHook); if (rv) goto err_out_free_netdev; #ifdef CONFIG_STA_SUPPORT //配置为sta模式 /* pAd->StaCfg.OriDevType = net_dev->type; */ RTMP_DRIVER_STA_DEV_TYPE_SET(pAd, net_dev->type); #endif /* CONFIG_STA_SUPPORT */ /*#ifdef KTHREAD_SUPPORT */ #ifdef PRE_ASSIGN_MAC_ADDR //设置固定的mac地址 UCHAR PermanentAddress[MAC_ADDR_LEN]; RTMP_DRIVER_MAC_ADDR_GET(pAd, &PermanentAddress[0]); DBGPRINT(RT_DEBUG_TRACE, ("@%s MAC address: %02x:%02x:%02x:%02x:%02x:%02x ", __FUNCTION__, PermanentAddress[0], PermanentAddress[1],PermanentAddress[2],PermanentAddress[3],PermanentAddress[4],PermanentAddress[5])); /* Set up the Mac address */ RtmpOSNetDevAddrSet(OpMode, net_dev, &PermanentAddress[0], NULL); #endif /* PRE_ASSIGN_MAC_ADDR */ DBGPRINT(RT_DEBUG_TRACE, ("<=== rt2860_probe ")); return 0; /* probe ok */ /* --------------------------- ERROR HANDLE --------------------------- */ err_out_free_netdev: RtmpOSNetDevFree(net_dev); err_out_free_radev: /* free RTMP_ADAPTER strcuture and os_cookie*/ RTMPFreeAdapter(pAd); err_out_iounmap: iounmap((void *)(csr_addr)); release_mem_region(pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); err_out_free_res: pci_release_regions(pci_dev); err_out: pci_disable_device(pci_dev); DBGPRINT(RT_DEBUG_ERROR, ("<=== rt2860_probe failed with rv = %d! ", rv)); return -ENODEV; /* probe fail */ } 其中重要的部分是
RTMPAllocAdapterBlock函数,该函数分配网络设备。
RtmpPhyNetDevInit函数,该函数实现了驱动底层设备的初始化。
CFG80211_Register函数是kernel中cfg80211的无线网络设备的注册接口。
RtmpOSNetDevAttach函数,该函数实现了网络设备的创建。
RtmpOSNetDevAddrSet函数,该函数设置了网络设备的固定mac地址。 其中CFG80211_Register函数,是无线无线网络的设备的初始化和接口注册,内容如下: /* ======================================================================== Routine Description: Register MAC80211 Module. Arguments: pAdCB - WLAN control block pointer pDev - Generic device interface pNetDev - Network device Return Value: NONE Note: pDev != pNetDev #define SET_NETDEV_DEV(net, pdev) ((net)->dev.parent = (pdev)) Can not use pNetDev to replace pDev; Or kernel panic. ======================================================================== */ BOOLEAN CFG80211_Register( IN VOID *pAd, IN struct device *pDev, IN struct net_device *pNetDev) { CFG80211_CB *pCfg80211_CB = NULL; CFG80211_BAND BandInfo; /* allocate MAC80211 structure */ os_alloc_mem(NULL, (UCHAR **)&pCfg80211_CB, sizeof(CFG80211_CB)); if (pCfg80211_CB == NULL) { DBGPRINT(RT_DEBUG_ERROR, ("80211> Allocate MAC80211 CB fail! ")); return FALSE; } /* End of if */ /* allocate wireless device */ RTMP_DRIVER_80211_BANDINFO_GET(pAd, &BandInfo); pCfg80211_CB->pCfg80211_Wdev = CFG80211_WdevAlloc(pCfg80211_CB, &BandInfo, pAd, pDev); if (pCfg80211_CB->pCfg80211_Wdev == NULL) { DBGPRINT(RT_DEBUG_ERROR, ("80211> Allocate Wdev fail! ")); os_free_mem(NULL, pCfg80211_CB); return FALSE; } /* End of if */ /* bind wireless device with net device */ #ifdef CONFIG_AP_SUPPORT /* default we are AP mode */ pCfg80211_CB->pCfg80211_Wdev->iftype = NL80211_IFTYPE_AP; #endif /* CONFIG_AP_SUPPORT */ #ifdef CONFIG_STA_SUPPORT /* default we are station mode */ pCfg80211_CB->pCfg80211_Wdev->iftype = NL80211_IFTYPE_STATION; #endif /* CONFIG_STA_SUPPORT */ pNetDev->ieee80211_ptr = pCfg80211_CB->pCfg80211_Wdev; SET_NETDEV_DEV(pNetDev, wiphy_dev(pCfg80211_CB->pCfg80211_Wdev->wiphy)); pCfg80211_CB->pCfg80211_Wdev->netdev = pNetDev; #ifdef RFKILL_HW_SUPPORT wiphy_rfkill_start_polling(pCfg80211_CB->pCfg80211_Wdev->wiphy); #endif /* RFKILL_HW_SUPPORT */ RTMP_DRIVER_80211_CB_SET(pAd, pCfg80211_CB); CFG80211DBG(RT_DEBUG_ERROR, ("80211> CFG80211_Register ")); return TRUE; } /* End of CFG80211_Register */ 其中重要的内容:
CFG80211_RegNotifier函数是驱动网络调度的通知回调函数。
CFG80211_SupBandInit 函数初始了无线设备的chinnel
CFG80211_WdevAlloc 函数实习了无线网络wiphy的分配和注册。 /* ======================================================================== Routine Description: Allocate a wireless device. Arguments: pAd - WLAN control block pointer pDev - Generic device interface Return Value: wireless device ======================================================================== */ static struct wireless_dev *CFG80211_WdevAlloc( IN CFG80211_CB *pCfg80211_CB, IN CFG80211_BAND *pBandInfo, IN VOID *pAd, IN struct device *pDev) { struct wireless_dev *pWdev; ULONG *pPriv; /* * We're trying to have the following memory layout: * * +------------------------+ * | struct wiphy | * +------------------------+ * | pAd pointer | * +------------------------+ */ pWdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); if (pWdev == NULL) { DBGPRINT(RT_DEBUG_ERROR, ("80211> Wireless device allocation fail! ")); return NULL; } /* End of if */ pWdev->wiphy = wiphy_new(&CFG80211_Ops, sizeof(ULONG *)); if (pWdev->wiphy == NULL) { DBGPRINT(RT_DEBUG_ERROR, ("80211> Wiphy device allocation fail! ")); goto LabelErrWiphyNew; } /* End of if */ /* keep pAd pointer */ pPriv = (ULONG *)(wiphy_priv(pWdev->wiphy)); *pPriv = (ULONG)pAd; set_wiphy_dev(pWdev->wiphy, pDev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) pWdev->wiphy->max_scan_ssids = pBandInfo->MaxBssTable; #endif /* KERNEL_VERSION */ #ifdef CONFIG_AP_SUPPORT pWdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP); #ifdef WDS_SUPPORT pWdev->wiphy->interface_modes | = BIT(NL80211_IFTYPE_WDS); #endif /* WDS_SUPPORT */ #endif /* CONFIG_STA_SUPPORT */ #ifdef CONFIG_STA_SUPPORT pWdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MONITOR); #endif /* CONFIG_STA_SUPPORT */ pWdev->wiphy->reg_notifier = CFG80211_RegNotifier; /* init channel information */ CFG80211_SupBandInit(pCfg80211_CB, pBandInfo, pWdev->wiphy, NULL, NULL); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) /* CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm) */ pWdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; #endif /* KERNEL_VERSION */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) pWdev->wiphy->cipher_suites = CipherSuites; pWdev->wiphy->n_cipher_suites = ARRAY_SIZE(CipherSuites); #endif /* LINUX_VERSION_CODE */ if (wiphy_register(pWdev->wiphy) < 0) { DBGPRINT(RT_DEBUG_ERROR, ("80211> Register wiphy device fail! ")); goto LabelErrReg; } /* End of if */ return pWdev; LabelErrReg: wiphy_free(pWdev->wiphy); LabelErrWiphyNew: os_free_mem(NULL, pWdev); return NULL; } /* End of CFG80211_WdevAlloc */ 其中wiphy_new函数分配了wiphy并关联了CFG80211_OpsCFG80211_Opscfg80211_ops结构体,是对无线设备接口的操作结构体。
wiphy_register 函数注册了无线设备的wiphy,建立无线网络设备接口。 CFG80211_Ops cfg80211提供的无线网络设备操作结构体,在rt2860v2的定义如下: struct cfg80211_ops CFG80211_Ops = { /* set channel for a given wireless interface */ .set_channel = CFG80211_OpsChannelSet, /* change type/configuration of virtual interface */ .change_virtual_intf = CFG80211_OpsVirtualInfChg, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) /* request to do a scan */ /* Note: must exist whatever AP or STA mode; Or your kernel will crash in v2.6.38. */ .scan = CFG80211_OpsScan, #endif /* LINUX_VERSION_CODE */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31)) #ifdef CONFIG_STA_SUPPORT /* join the specified IBSS (or create if necessary) */ .join_ibss = CFG80211_OpsIbssJoin, /* leave the IBSS */ .leave_ibss = CFG80211_OpsIbssLeave, #endif /* CONFIG_STA_SUPPORT */ #endif /* LINUX_VERSION_CODE */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) /* set the transmit power according to the parameters */ .set_tx_power = CFG80211_OpsTxPwrSet, /* store the current TX power into the dbm variable */ .get_tx_power = CFG80211_OpsTxPwrGet, /* configure WLAN power management */ .set_power_mgmt = CFG80211_OpsPwrMgmt, /* get station information for the station identified by @mac */ .get_station = CFG80211_OpsStaGet, /* dump station callback */ .dump_station = CFG80211_OpsStaDump, /* notify that wiphy parameters have changed */ .set_wiphy_params = CFG80211_OpsWiphyParamsSet, /* add a key with the given parameters */ .add_key = CFG80211_OpsKeyAdd, /* get information about the key with the given parameters */ .get_key = CFG80211_OpsKeyGet, /* remove a key given the @mac_addr */ .del_key = CFG80211_OpsKeyDel, /* set the default key on an interface */ .set_default_key = CFG80211_OpsKeyDefaultSet, #ifdef CONFIG_STA_SUPPORT /* connect to the ESS with the specified parameters */ .connect = CFG80211_OpsConnect, /* disconnect from the BSS/ESS */ .disconnect = CFG80211_OpsDisconnect, #endif /* CONFIG_STA_SUPPORT */ #endif /* LINUX_VERSION_CODE */ #ifdef RFKILL_HW_SUPPORT /* polls the hw rfkill line */ .rfkill_poll = CFG80211_OpsRFKill, #endif /* RFKILL_HW_SUPPORT */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33)) /* get site survey information */ .dump_survey = CFG80211_OpsSurveyGet, /* cache a PMKID for a BSSID */ .set_pmksa = CFG80211_OpsPmksaSet, /* delete a cached PMKID */ .del_pmksa = CFG80211_OpsPmksaDel, /* flush all cached PMKIDs */ .flush_pmksa = CFG80211_OpsPmksaFlush, #endif /* LINUX_VERSION_CODE */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34)) /* Request the driver to remain awake on the specified channel for the specified duration to complete an off-channel operation (e.g., public action frame exchange). */ .remain_on_channel = NULL, /* cancel an on-going remain-on-channel operation */ .cancel_remain_on_channel = NULL, #if (LINUX_VERSION_CODE == KERNEL_VERSION(2,6,34)) /* transmit an action frame */ .action = NULL, #endif /* LINUX_VERSION_CODE */ #endif /* LINUX_VERSION_CODE */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) /* configure connection quality monitor RSSI threshold */ .set_cqm_rssi_config = NULL, #endif /* LINUX_VERSION_CODE */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) /* notify driver that a management frame type was registered */ .mgmt_frame_register = NULL, #endif /* LINUX_VERSION_CODE */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)) /* set antenna configuration (tx_ant, rx_ant) on the device */ .set_antenna = NULL, /* get current antenna configuration from device (tx_ant, rx_ant) */ .get_antenna = NULL, #endif /* LINUX_VERSION_CODE */ };