Chinaunix首页 | 论坛 | 博客
  • 博客访问: 36487
  • 博文数量: 44
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 501
  • 用 户 组: 普通用户
  • 注册时间: 2017-03-09 09:24
个人简介

水滴

文章分类

全部博文(44)

文章存档

2019年(2)

2018年(32)

2017年(10)

我的朋友

分类: LINUX

2017-12-08 09:27:21

mtk7621驱动

无线驱动在完成驱动注册的同时,需要进行 cfg80211接口注册(提供命令支持)。

1. 驱动

mtk wifi驱动基于pci进行扩展,第一个文件:/os/linux/pci_main_dev.c
文件用于创建和注册基于pci接口的网络设备,PCI设备上有三种地址空间:PCI的I/O空间、PCI的存储空间和PCI的配置空间。CPU可以访问PCI设备上的所有地址空间,其中I/O空间和存储空间提供给设备驱动程序使用,而配置空间则由Linux内核中的PCI初始化代码使用。内核在启动时负责对所有PCI设备进行初始化,配置好所有的PCI设备,包括中断号以及I/O基址,并在文件/proc/pci中列出所有找到的PCI设备,以及这些设备的参数和属性。
Linux驱动程序通常使用结构(struct)来表示一种设备,而结构体中的变量则代表某一具体设备,该变量存放了与该设备相关的所有信息。好的驱动程序都应该能驱动多个同种设备,每个设备之间用次设备号进行区分,如果采用结构数据来代表所有能由该驱动程序驱动的设备,那么就可以简单地使用数组下标来表示次设备号。

1.1 数据结构

1 static struct pci_device_id rt_pci_tbl[] DEVINITDATA =
{
{PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC3091_PCIe_DEVICE_ID)},
#ifdef RT8592
{PCI_DEVICE(NIC_PCI_VENDOR_ID, NIC8592_PCIe_DEVICE_ID)},
#endif /* RT8592 */
#ifdef MT76x2
{PCI_DEVICE(0x1400, NIC7650_PCIe_DEVICE_ID)},
{PCI_DEVICE(0x1400, NIC7662_PCIe_DEVICE_ID)},
{PCI_DEVICE(MTK_PCI_VENDOR_ID, NIC7662_PCIe_DEVICE_ID)},
{PCI_DEVICE(MTK_PCI_VENDOR_ID, NIC7632_PCIe_DEVICE_ID)},
{PCI_DEVICE(MTK_PCI_VENDOR_ID, NIC7612_PCIe_DEVICE_ID)},
{PCI_DEVICE(MTK_PCI_VENDOR_ID, NIC7602_PCIe_DEVICE_ID)},
#endif /* MT76x2 */
{} /* terminate list */
};
struct pci_device_id {
         __u32 vendor,device;                    //厂商和设备ID
         __u32 subvendor,subdevice;          //子系统和设备ID
         __u32 class,class_mask;                //类、子类、prog-if三元组
         kernel_ulong_t driver_data;           //驱动私有数据
pci_device_id 用MODULE_DEVICE_TABLE映射到用户空间。

2 struct pci_driver {
struct list_head node;
const char *name;
const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */
int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
void (*shutdown) (struct pci_dev *dev);
int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* PF pdev */
const struct pci_error_handlers *err_handler;
struct device_driver driver;
struct pci_dynids dynids;
};

3 struct pci_dev {
struct list_head bus_list;
/* 总线设备链表元素bus_list:每一个pci_dev结构除了链接到全局设备链表中外,还会通过这个成员连接到其所属PCI总线的设备链表中。每一条PCI总线都维护一条它自己的设备链表视图,以便描述所有连接在该PCI总线上的设备,其表头由PCI总线的pci_bus结构中的 devices成员所描述t*/
struct pci_bus *bus;
/* 总线指针bus:指向这个PCI设备所在的PCI总线的pci_bus结构。因此,对于桥设备而言,bus指针将指向桥设备的主总线(primary bus),也即指向桥设备所在的PCI总线*/
struct pci_bus *subordinate;
/* 指针subordinate:指向这个PCI设备所桥接的下级总线。这个指针成员仅对桥设备才有意义,而对于一般的非桥PCI设备而言,该指针成员总是为NULL*/

void *sysdata;
/* 无类型指针sysdata:指向一片特定于系统的扩展数据*/

struct proc_dir_entry *procent;
/* 指针procent:指向该PCI设备在/proc文件系统中对应的目录项*/

unsigned int devfn;
/* devfn:这个PCI设备的设备功能号,也成为PCI逻辑设备号(0-255)。其中bit[7:3]是物理设备号(取值范围0-31),bit[2:0]是功能号(取值范围0-7)。 */

unsigned short vendor;
/* vendor:这是一个16无符号整数,表示PCI设备的厂商ID*/

unsigned short device;
/*device:这是一个16无符号整数,表示PCI设备的设备ID */

unsigned short subsystem_vendor;
/* subsystem_vendor:这是一个16无符号整数,表示PCI设备的子系统厂商ID*/

unsigned short subsystem_device;
/* subsystem_device:这是一个16无符号整数,表示PCI设备的子系统设备ID。*/

unsigned int class;
/* class:32位的无符号整数,表示该PCI设备的类别,其中,bit[7:0]为编程接口,bit[15:8]为子类别代码,bit [23:16]为基类别代码,bit[31:24]无意义。显然,class成员的低3字节刚好对应与PCI配置空间中的类代码*/
u8 revision; /* PCI revision, low byte of class word */
u8 hdr_type; /* PCI header type (`multi' flag masked out) */
u8 pcie_cap; /* PCI-E capability offset */
u8 msi_cap; /* MSI capability offset */
u8 msix_cap; /* MSI-X capability offset */
u8 pcie_mpss:3; /* PCI-E Max Payload Size Supported */
u8 rom_base_reg; /* which config register controls the ROM */
u8 pin; /* which interrupt pin this device uses */
u16 pcie_flags_reg; /* cached PCI-E Capabilities Register */

struct pci_driver *driver;
/* 指针driver:指向这个PCI设备所对应的驱动程序定义的pci_driver结构。每一个pci设备驱动程序都必须定义它自己的pci_driver结构来描述它自己。*/

u64 dma_mask;
/*dma_mask:用于DMA的总线地址掩码,一般来说,这个成员的值是0xffffffff。数据类型dma_addr_t定义在include/asm/types.h中,在x86平台上,dma_addr_t类型就是u32类型*/

struct device_dma_parameters dma_parms;

pci_power_t current_state;
/* 当前操作状态 */
u8 pm_cap; /* PM capability offset */
unsigned int pme_support:5; /* Bitmask of states from which PME#
can be generated */
unsigned int pme_interrupt:1;
unsigned int pme_poll:1; /* Poll device's PME status bit */
unsigned int d1_support:1; /* Low power state D1 is supported */
unsigned int d2_support:1; /* Low power state D2 is supported */
unsigned int no_d1d2:1; /* D1 and D2 are forbidden */
unsigned int no_d3cold:1; /* D3cold is forbidden */
unsigned int d3cold_allowed:1; /* D3cold is allowed by user */
unsigned int mmio_always_on:1; /* disallow turning off io/mem
decoding during bar sizing */
unsigned int wakeup_prepared:1;
unsigned int runtime_d3cold:1; /* whether go through runtime
D3cold, not set for devices
powered on/off by the
corresponding bridge */
unsigned int d3_delay; /* D3->D0 transition time in ms */
unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */

pci_channel_state_t error_state; /* current connectivity state */
struct device dev; /* Generic device interface */

int cfg_size;
/* 配置空间的大小 */

unsigned int irq;
struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */

bool match_driver; /* Skip attaching driver */
/* These fields are used by common fixups */
unsigned int transparent:1; /* Transparent PCI bridge */
unsigned int multifunction:1;/* Part of multi-function device */
/* keep track of device state */
unsigned int is_added:1;
unsigned int is_busmaster:1; /* device is busmaster */
unsigned int no_msi:1; /* device may not use msi */
unsigned int block_cfg_access:1; /* config space access is blocked */
unsigned int broken_parity_status:1; /* Device generates false positive parity */
unsigned int irq_reroute_variant:2; /* device needs IRQ rerouting variant */
unsigned int msi_enabled:1;
unsigned int msix_enabled:1;
unsigned int ari_enabled:1; /* ARI forwarding */
unsigned int is_managed:1;
unsigned int is_pcie:1; /* Obsolete. Will be removed.
Use pci_is_pcie() instead */
unsigned int needs_freset:1; /* Dev requires fundamental reset */
unsigned int state_saved:1;
unsigned int is_physfn:1;
unsigned int is_virtfn:1;
unsigned int reset_fn:1;
unsigned int is_hotplug_bridge:1;
unsigned int __aer_firmware_first_valid:1;
unsigned int __aer_firmware_first:1;
unsigned int broken_intx_masking:1;
unsigned int io_window_1k:1; /* Intel P2P bridge 1K I/O windows */
pci_dev_flags_t dev_flags;
atomic_t enable_cnt; /* pci_enable_device has been called */

u32 saved_config_space[16]; /* config space saved at suspend time */
struct hlist_head saved_cap_space;
struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
int rom_attr_enabled; /* has display of the rom attribute been enabled? */
struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */
#ifdef CONFIG_PCI_MSI
struct list_head msi_list;
struct kset *msi_kset;
#endif
struct pci_vpd *vpd;
#ifdef CONFIG_PCI_ATS
union {
struct pci_sriov *sriov; /* SR-IOV capability related */
struct pci_dev *physfn; /* the PF this VF is associated with */
};
struct pci_ats *ats; /* Address Translation Service */
#endif
phys_addr_t rom; /* Physical address of ROM if it's not from the BAR */
size_t romlen; /* Length of ROM if it's not from the BAR
};

1.2关键函数

1.2.1 rt_pci_init_module

调用函数pci_register_driver(&rt_pci_driver); 注册设备

1.2.2 rt_pci_probe

主要函数分析
1 rv = pci_enable_device(pdev)
唤醒和使能设备

2 rv = pci_request_regions(pdev, print_name)
函数通知内核,当前PCI将使用这些内存地址,其他设备不能再使用了

3 csr_addr = (unsigned long) ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
物理地址与虚拟地址进行映射

4 pci_set_master(pdev);
设置设备具有获得总线的能力,即调用这个函数,使设备具备申请使用PCI总线的能力

5 rv = RTMPAllocAdapterBlock(handle, &pAd);
适配器申请内存, 并进行初始化。(包括beaconbuf申请\rx ring \tx ring 等初始化)。

6 RTMP_DRIVER_PCI_CSR_SET(pAd, csr_addr);
设置pci 对应的虚拟地址映射

7 RTMP_DRIVER_PCIE_INIT(pAd, pdev);
设置wlan芯片相关寄存器设置 以及pcie的初始化,调用mac目录下的接口函数,对寄存器进行设置.

8 net_dev = RtmpPhyNetDevInit(pAd, &netDevHook);
实现物理层网络设备的创建、初始化、以及相关结构接口的初始化,主要是结构体的创建。

9 CFG80211_Register(pAd, &(pdev->dev), net_dev);
实现cfg80211网络设备模块的注册

10 RTMP_DRIVER_OP_MODE_GET(pAd, &OpMode);
获取当前设备wlan设备的工作模式

11 rv = RtmpOSNetDevAttach(OpMode, net_dev, &netDevHook);
实现网络设备相应回调函数的初始化以及网络设备的注册

12 RtmpOSNetDevAddrSet(OpMode, net_dev, &mac_addr[0], NULL);
设置网络设备的mac地址
注释: 一般调用ioctl接口实现一些相关命令处理,函数接口 RTMP_COM_IoctlHandle()
位置/src/common/comm_cfg.c文件

1.2.2.1 RTMP_DRIVER_PCIE_INIT(pAd, pdev)->RTMPInitPCIeDevice()
函数通过ioctl调用RTMPInitPCIeDevice(pData, pAd);进行初始化

1.2.2.2 net_dev = RtmpPhyNetDevInit(pAd, &netDevHook);

1 RTMP_DRIVER_MAIN_INF_GET(pAd, &InfId);
获取主设备的id INIT_MAIN 0x0100。

2 RTMP_DRIVER_MAIN_INF_CREATE(pAd, &net_dev);
调用CMD_RTPRIV_IOCTL_INF_MAIN_CREATE 实现网络设备内存的申请

3 初始化回调函数

4 RTMP_DRIVER_NET_DEV_SET(pAd, net_dev); 对apcfg结构进行初始化

1.2.2.3 CFG80211_Register(pAd, &(pdev->dev), net_dev);
实现CFG80211接口注册。

1.2.2.4 rv = RtmpOSNetDevAttach(OpMode, net_dev, &netDevHook);
函数RtmpPhyNetDevInit已经完成相应结构的初始化,接下来进行网络设备的注册
1 调用函数 register_netdev(pNetDev);
实现网络设备注册

1.2.3 rt28xx_open

rt_pci_resume驱动唤醒调用函数,
rt_pci_resume ->rt28xx_open
1 RtmpOSIRQRequest(net_dev);
申请中断,并设置中断处理函数rt2860_interrupt()
注释: 函数rt2860_interrupt根据中断信号源,利用函数(RTMP_OS_TASKLET_SCHE)设备添加任务队列。

2 RTMP_DRIVER_IRQ_INIT(pAd);
中断初始化, 设置寄存器。

3 rt28xx_init(pAd, mac, hostname)
读取配置信息、实现相关wifi芯片初始化操作。

4 RT28xx_MBSS_Init(pAd, net_dev);
初始化虚拟网络接口,注册相关回调函数,并注册网络

5 RT28xx_WDS_Init(pAd, net_dev);
支持wds功能,是两个无线设备之间通过wds功能,实现无线扩展。

6 RT28xx_ApCli_Init
初始化apcli

7 RT28xx_Monitor_Init(pAd, net_dev);
初始化嗅探功能

8 RTMP_CFG80211_DummyP2pIf_Init(pAd);
初始化p2p, 使在没有网络的情况下,wifi设备之间彼此互联。

9 RTMP_DRIVER_CFG80211_START(pAd);
cfg80211

10 RTMPDrvOpen(pAd);
使能设备,开启设备收发等功能,配置相应寄存器。

1.2.3.1 rt28xx_init

关键函数分析
1 rtmp_asic_top_init
设备wifi芯片mac\ power等相关寄存器

2 分配bsstable channel 结构
IF_DEV_CONFIG_OPMODE_ON_AP(pAd)
{
AutoChBssTableInit(pAd);
ChannelInfoInit(pAd);
}

3 Status = RTMPInitTxRxRingMemory(pAd);
初始化接收发送环链表

4 RtmpMgmtTaskInit
初始化管理任务,注册回调函数 RTPCICmdThread->VOID CMDHandler(RTMP_ADAPTER *pAd) 执行相应命令处理

5 MlmeInit(RTMP_ADAPTER *pAd)
重点:->MlmeHandler(pAd)实现任务处理
初始化mlme任务、相关数据、以及各状态机
如果auth 报文 assoc报文等处理函数
802.11MAC主要分为数据、管理部分;
数据部分:数据包的收发接口
管理部分:媒介访问控制、扫描、认证与保密、关联、电源管理、定时器同步等。

6 UserCfgInit(pAd);
实现相关结构的默认初始化,如station、ap等

7 Status = RtmpNetTaskInit(pAd);
重点:
1) 初始化网络任务,注册相应回调函数,

2)如数据接收,调用函数rx_done_tasklet 实现数据接收处理, 最终调用函数
数据报文APHandleRxDataFrame、
管理报文 dev_rx_mgmt_frm->REPORT_MGMT_FRAME_TO_MLME();
控制报文 dev_rx_ctrl_frm(pAd, &rxblk);

3) 如数据发送,调用ac1_dma_done_tasklet实现数据的发送

8. MeasureReqTabInit(pAd) TpcReqTabInit(pAd)
完成相应结构的内存申请

9. Status = RTMPReadParametersHook(pAd);

文件位置"/etc/Wireless/RT2860/RT2860.dat"
从配置文件读取配置信息,完成相关参数初始化。

。。。。。。做相关初始化
10 调用函数APStartUp(pAd); 启动AP
阅读(474) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册