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.c
和
pci_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
}
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总线上。
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
,该函数内容如下:
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
"));
if ((rv = pci_enable_device(pci_dev))!= 0)
{
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)
{
DBGPRINT(RT_DEBUG_ERROR, ("Request PCI resource failed, errno=%d!
", rv));
goto err_out;
}
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));
}
pci_set_master(pci_dev);
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;
RTMP_DRV_OPS_FUNCTION(pRtmpDrvOps, NULL, &PciConfig, NULL);
}
#endif /* OS_ABL_FUNC_SUPPORT */
rv = RTMPAllocAdapterBlock(handle, &pAd);
if (rv != NDIS_STATUS_SUCCESS)
goto err_out_iounmap;
RTMP_DRIVER_PCI_CSR_SET(pAd, csr_addr);
RTMP_DRIVER_PCIE_INIT(pAd, pci_dev);
net_dev = RtmpPhyNetDevInit(pAd, &netDevHook);
if (net_dev == NULL)
goto err_out_free_radev;
net_dev->irq = pci_dev->irq;
net_dev->base_addr = csr_addr;
pci_set_drvdata(pci_dev, net_dev);
#ifdef NATIVE_WPA_SUPPLICANT_SUPPORT
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
SET_NETDEV_DEV(net_dev, &(pci_dev->dev));
#endif
#endif /* NATIVE_WPA_SUPPLICANT_SUPPORT */
#ifdef RT_CFG80211_SUPPORT
{
CFG80211_Register(pAd, &(pci_dev->dev), net_dev);
}
#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模式
RTMP_DRIVER_STA_DEV_TYPE_SET(pAd, net_dev->type);
#endif /* CONFIG_STA_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]));
RtmpOSNetDevAddrSet(OpMode, net_dev, &PermanentAddress[0], NULL);
#endif /* PRE_ASSIGN_MAC_ADDR */
DBGPRINT(RT_DEBUG_TRACE, ("<=== rt2860_probe
"));
return 0;
err_out_free_netdev:
RtmpOSNetDevFree(net_dev);
err_out_free_radev:
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;
}
其中重要的部分是
RTMPAllocAdapterBlock
函数,该函数分配网络设备。
RtmpPhyNetDevInit
函数,该函数实现了驱动底层设备的初始化。
CFG80211_Register
函数是kernel中cfg80211的无线网络设备的注册接口。
RtmpOSNetDevAttach
函数,该函数实现了网络设备的创建。
RtmpOSNetDevAddrSet
函数,该函数设置了网络设备的固定mac地址。
其中
CFG80211_Register
函数,是无线无线网络的设备的初始化和接口注册,内容如下:
BOOLEAN CFG80211_Register(
IN VOID *pAd,
IN struct device *pDev,
IN struct net_device *pNetDev)
{
CFG80211_CB *pCfg80211_CB = NULL;
CFG80211_BAND BandInfo;
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;
}
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;
}
#ifdef CONFIG_AP_SUPPORT
pCfg80211_CB->pCfg80211_Wdev->iftype = NL80211_IFTYPE_AP;
#endif
#ifdef CONFIG_STA_SUPPORT
pCfg80211_CB->pCfg80211_Wdev->iftype = NL80211_IFTYPE_STATION;
#endif
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
RTMP_DRIVER_80211_CB_SET(pAd, pCfg80211_CB);
CFG80211DBG(RT_DEBUG_ERROR, ("80211> CFG80211_Register
"));
return TRUE;
}
其中重要的内容:
CFG80211_RegNotifier
函数是驱动网络调度的通知回调函数。
CFG80211_SupBandInit
函数初始了无线设备的chinnel
CFG80211_WdevAlloc
函数实习了无线网络wiphy的分配和注册。
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;
pWdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
if (pWdev == NULL)
{
DBGPRINT(RT_DEBUG_ERROR, ("80211> Wireless device allocation fail!
"));
return NULL;
}
pWdev->wiphy = wiphy_new(&CFG80211_Ops, sizeof(ULONG *));
if (pWdev->wiphy == NULL)
{
DBGPRINT(RT_DEBUG_ERROR, ("80211> Wiphy device allocation fail!
"));
goto LabelErrWiphyNew;
}
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
#ifdef CONFIG_AP_SUPPORT
pWdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP);
#ifdef WDS_SUPPORT
pWdev->wiphy->interface_modes | = BIT(NL80211_IFTYPE_WDS);
#endif
#endif
#ifdef CONFIG_STA_SUPPORT
pWdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MONITOR);
#endif
pWdev->wiphy->reg_notifier = CFG80211_RegNotifier;
CFG80211_SupBandInit(pCfg80211_CB, pBandInfo, pWdev->wiphy, NULL, NULL);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30))
pWdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
pWdev->wiphy->cipher_suites = CipherSuites;
pWdev->wiphy->n_cipher_suites = ARRAY_SIZE(CipherSuites);
#endif
if (wiphy_register(pWdev->wiphy) < 0)
{
DBGPRINT(RT_DEBUG_ERROR, ("80211> Register wiphy device fail!
"));
goto LabelErrReg;
}
return pWdev;
LabelErrReg:
wiphy_free(pWdev->wiphy);
LabelErrWiphyNew:
os_free_mem(NULL, pWdev);
return NULL;
}
其中
wiphy_new
函数分配了wiphy并关联了
CFG80211_Ops
,
CFG80211_Ops
是
cfg80211_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,
/* 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,
/* join the specified IBSS (or create if necessary) */
.join_ibss = CFG80211_OpsIbssJoin,
/* leave the IBSS */
.leave_ibss = CFG80211_OpsIbssLeave,
/* 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,
/* connect to the ESS with the specified parameters */
.connect = CFG80211_OpsConnect,
/* disconnect from the BSS/ESS */
.disconnect = CFG80211_OpsDisconnect,
/* polls the hw rfkill line */
.rfkill_poll = CFG80211_OpsRFKill,
/* 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,
/*
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,
/* transmit an action frame */
.action = NULL,
/* configure connection quality monitor RSSI threshold */
.set_cqm_rssi_config = NULL,
/* notify driver that a management frame type was registered */
.mgmt_frame_register = NULL,
/* 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,
};