现在的位置: 首页 > 综合 > 正文

android_wifi读书笔记之7-wifi驱动解析

2018年08月23日 ⁄ 综合 ⁄ 共 6237字 ⁄ 字号 评论关闭

本文为读书笔记,整理自网络文献和源码

7、wifi驱动解析

7.1、WIFI内核实现的大致框架

Linux下已经支持了市面上的大多数wifi卡的驱动

每种wifi卡都是需要固件(firmware)才能驱动的,并且同一种卡工作在不同接口时对应的firmware是不一样的

需要注意的是:很多firmware都是要花钱的

固件通过其驱动下载到无线卡中,才能驱动起来


wifi驱动的通用的软件架构

1. 分为两部分,上面为主机端驱动,下面是我们之前所说的firmware

2. 其中固件部分的主要工作是:因为天线接受和发送回来的都是802.11帧的帧,而主机接受和传送出来的数据都必须是802.3的帧,所以必须由firmware来负责802.3的帧和802.11帧之间的转换,

3. 当天线收到数据,并被firmware处理好后会放在一个buffer里,并产生一个中断,主机在收到中断后就去读这个buffer

 

7.2 平台总线下的wifi设备和驱动:

设备:

structplatform_device brcm_device_wlan = {

        .name           = "bcmdhd_wlan",

        .id             = 1,

        .num_resources  = ARRAY_SIZE(brcm_wlan_resources),

        .resource       = brcm_wlan_resources,

        .dev            = {

                .platform_data =&brcm_wlan_control,

        },

};

驱动:

staticstruct platform_driver wifi_device = {

         .probe          = wifi_probe,

         .remove         = wifi_remove,

         .suspend        = wifi_suspend,

         .resume         = wifi_resume,

         .driver         = {

         .name  = "bcmdhd_wlan",

         }

};

platform_driver_register(&wifi_device);

platform_driver_register(&wifi_device)被调用流程:

 

late_initcall(dhd_module_init);

dhd_module_init

àwl_android_wifictrl_func_add

àwifi_add_dev

àplatform_driver_register(&wifi_device)

 

 

 

7.3 Sdio总线下的wifi设备和驱动:

drivers/net/wireless/bcmdhd/bcmsdh_linux.c

late_initcall(dhd_module_init);

dhd_module_init

àdhd_bus_register() // Dhd_sdio.c

àbcmsdh_register(&dhd_sdio); //Bcmsdh_linux.c  

                   à sdio_function_init// bcmsdh_sdmmc_linux.c

                            àsdio_register_driver // Sdio_bus.c

                                     à driver_register(&drv->drv);

                                               à bus_add_driver(drv);

                                               àdriver_add_groups(drv,drv->groups);

或者:

         #ifdef BCMSDH_MODULE

         module_init(bcmsdh_module_init);

         bcmsdh_module_init(void)

                   à sdio_function_init

                            àsdio_register_driver

                                     ……(同上)

Sdio设备的驱动由sdio_driver结构体定义,sdio_register_driver函数将该设备驱动挂载到sdio_bus_type总线上。

/* * SDIOfunction device driver  */

structsdio_driver {

         char *name; //设备名

         const struct sdio_device_id *id_table; //设备驱动ID

         int (*probe)(struct sdio_func *, conststruct sdio_device_id *); //匹配函数

         void (*remove)(struct sdio_func *);

         struct device_driver drv;

};

 

/* bcmsdh_sdmmc_linux.c*/

staticstruct sdio_driver bcmsdh_sdmmc_driver = {

         .probe                =bcmsdh_sdmmc_probe,

         .remove            =bcmsdh_sdmmc_remove,

         .name                ="bcmsdh_sdmmc",

         .id_table  = bcmsdh_sdmmc_ids,

#if(LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) &&defined(CONFIG_PM)

         .drv = {

                   .pm  = &bcmsdh_sdmmc_pm_ops,

         },

#endif /*(LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) &&defined(CONFIG_PM) */

};

error = sdio_register_driver(&bcmsdh_sdmmc_driver);

 

/**

 *     sdio_register_driver- register a function driver

 *     @drv:SDIO function driver

 */

intsdio_register_driver(struct sdio_driver *drv)

{

         drv->drv.name = drv->name;

         drv->drv.bus = &sdio_bus_type; //设置driver的bus为sdio_bus_type

         returndriver_register(&drv->drv);

}

 

staticstruct bus_type sdio_bus_type = {

         .name                ="sdio",

         .dev_attrs        = sdio_dev_attrs,

         .match               =sdio_bus_match,

         .uevent              =sdio_bus_uevent,

         .probe                =sdio_bus_probe,

         .remove            =sdio_bus_remove,

         .pm           = SDIO_PM_OPS_PTR,

};

注意:设备或者驱动注册到系统中的过程中,都会调用相应bus上的匹配函数来进行匹配合适的驱动或者设备,对于sdio设备的匹配是由sdio_bus_match和sdio_bus_probe函数来完成。

sdio_bus_match(structdevice *dev, struct device_driver *drv)

         àsdio_match_device(func, sdrv)

           àsdio_match_one(func, ids)

通过匹配id_table 和 sdio_driver设备驱动中id,来匹配合适的驱动或设备, 如果匹配成功那么会最终会调用.probe函数,来完成相关操作。

sdio_bus_probe

         àsdio_match_device(func, drv);

àret = drv->probe(func, id); //调用wifi驱动的probe函数,也就是bcmsdh_sdmmc_linux.c 中的bcmsdh_sdmmc_probe

bcmsdh_sdmmc_probe函数分析

bcmsdh_sdmmc_probe

àbcmsdh_probe

àbcmsdh_attach

à sdioh_attach

     à sdioh_sdmmc_card_enablefuncs(sd)

                                     à sdio_enable_func(gInstance->func[1])//使能sdio功能设备

到此wifi作为sdio设备的初始化完毕,sdio通道打通。后面就主要针对的是wifi作为网络设备初始化。

7.4  Wifi网络功能的初始化

Linux网络设备驱动中的重要数据结构:struct net_device和struct net_device_ops

在struct net_device_ops结构中声明了:

网络设备注册,启用,停止,发送数据帧,选择网卡队列(对于支持网卡多队列的),设置网络设备mac地址(如果此接口没有被定义将不会修改mac地址,那说明驱动要实现,网卡都需要支持才行,不过现在基本都支持),验证网络设备的mac地址,改变网络设备的MTU(如果该接口没有被定义则返回error,那说明驱动要实现,网卡要支持才行,不过现在基本都支持),获取网络设备的使用统计状态net_device_stats(比如统计发了多少个数据包,接受了多少个数据包,发了多少字节,接受了多少字节,多少坏包等等,详解下面net_device_stats数据结构,如果驱动没有定义此接口,dev->statswill
be used.

在:

staticstruct net_device_ops dhd_ops_pri = {

         .ndo_open = dhd_open,

         .ndo_stop = dhd_stop,

         .ndo_get_stats = dhd_get_stats,

         .ndo_do_ioctl = dhd_ioctl_entry,

         .ndo_start_xmit = dhd_start_xmit,

         .ndo_set_mac_address =dhd_set_mac_address,

         .ndo_set_multicast_list =dhd_set_multicast_list,

};

staticstruct net_device_ops dhd_ops_virt = {

         .ndo_get_stats = dhd_get_stats,

         .ndo_do_ioctl = dhd_ioctl_entry,

         .ndo_start_xmit = dhd_start_xmit,

         .ndo_set_mac_address =dhd_set_mac_address,

         .ndo_set_multicast_list =dhd_set_multicast_list,

};

 

staticconst struct net_device_ops wl_cfgp2p_if_ops = {

         .ndo_open                 = wl_cfgp2p_if_open,

         .ndo_stop                  = wl_cfgp2p_if_stop,

         .ndo_do_ioctl            = wl_cfgp2p_do_ioctl,

         .ndo_start_xmit                = wl_cfgp2p_start_xmit,

};

staticconst struct net_device_ops dhd_mon_if_ops = {

         .ndo_open                 = dhd_mon_if_open,

         .ndo_stop                  = dhd_mon_if_stop,

         .ndo_start_xmit                = dhd_mon_if_subif_start_xmit,

         .ndo_set_multicast_list =dhd_mon_if_set_multicast_list,

         .ndo_set_mac_address          = dhd_mon_if_change_mac,

};

 

其中dhd_ops_pri的初始化流程:

……

dhd_bus_register

àbcmsdh_register(&dhd_sdio);

static bcmsdh_driver_t dhd_sdio ={

         dhdsdio_probe,

         dhdsdio_disconnect

};

其中:dhdsdio_probe

àdhd_net_attach

     à net->netdev_ops =&dhd_ops_virt;

或者

              ànet->netdev_ops =&dhd_ops_pri;

 

将dhd_ops_pri赋给netdev_ops

 

intbcmsdh_probe(struct device *dev)

{

……

if(!(sdhc->ch = drvinfo.attach((vendevid>> 16),

                                          (vendevid& 0xFFFF), 0, 0, 0, 0,

                                         (void *)regs,NULL, sdh))) {

                   printk("%s: deviceattach failed\n", __FUNCTION__);

                   goto err;

         }

……

 

}

抱歉!评论已关闭.