wifi driver 学习笔记

来源:互联网 发布:华为算法工程师笔试 编辑:程序博客网 时间:2024/04/28 10:55
module_init(woal_init_module);          初始化wifi模块
module_exit(woal_cleanup_module);         卸载wifi模块

module_param(fw_name, charp, 0);            参数固件的名称
MODULE_PARM_DESC(fw_name, "Firmware name");    对fw_name进行描述
module_param(fw_crc_check, int, 1);
MODULE_PARM_DESC(fw_crc_check,

                 "1: Enable FW download CRC check (default); 0: Disable FW download CRC check");  

如果fw_crc_check的值为1,则固件下载进行循环冗余校验(默认);如果为0则不进行虚幻冗余校验

module_param(mac_addr, charp, 0);
MODULE_PARM_DESC(mac_addr, "MAC address");    mac_addr 为 物理地址
#ifdef MFG_CMD_SUPPORT
module_param(mfg_mode, int, 0);
MODULE_PARM_DESC(mfg_mode,
                 "0: Download normal firmware; 1: Download MFG firmware");如果mfg_mode的值为0则下载普通版的firmware,如果值为1则下载MFGfirmware
#endif /* MFG_CMD_SUPPORT */
module_param(drv_mode, int, 0);
MODULE_PARM_DESC(drv_mode, "1: STA; 2: UAP; 3: STA+UAP"); 驱动类型drv_mode=1为STA,drv_mode=2为UAP,drv_mode=3为STA和UAP综合
#ifdef DEBUG_LEVEL1
module_param(drvdbg, ulong, 0);
MODULE_PARM_DESC(drvdbg, "Driver debug");   drvdbg为 驱动调试
#endif /* DEBUG_LEVEL1 */
module_param(auto_ds, int, 0);
MODULE_PARM_DESC(auto_ds,

                 "0: MLAN default; 1: Enable auto deep sleep; 2: Disable auto deep sleep");

auto_ds=0就是MLAN默认的模式,auto_ds=1可进入深度睡眠模式, auto_ds=2禁止进入深度睡眠模式

module_param(ps_mode, int, 0);
MODULE_PARM_DESC(ps_mode,

                 "0: MLAN default; 1: Enable IEEE PS mode; 2: Disable IEEE PS mode");

ps_mode=0为MALN的默认模式ps_mode=1打开IEEE PS ps_mode =2为关闭IEEE PS(PS 可能代表的是物理时隙)

module_param(max_tx_buf, int, 0);
MODULE_PARM_DESC(max_tx_buf, "Maximum Tx buffer size (2048/4096/8192)"); 发送最大缓冲区间大小max_tx_buf的值可以是2048或4096或8192
#ifdef SDIO_SUSPEND_RESUME
module_param(pm_keep_power, int, 1);
MODULE_PARM_DESC(pm_keep_power, "1: PM keep power (default); 0: PM no power");电源管理pm_keep_power=1能管理,0失去管理能力
#endif
#if defined(STA_SUPPORT)
module_param(cfg_11d, int, 0);
MODULE_PARM_DESC(cfg_11d,
                 "0: MLAN default; 1: Enable 802.11d; 2: Disable 802.11d");cfg_11d=0代表MLAN默认的模式,cfg_11d=1代表打开802.11d,cfg_11d=2代表关闭802.11b
#endif


初始化MLAN模块,返回值为 MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE

static int
woal_init_module(void)
{
    int ret = (int) MLAN_STATUS_SUCCESS;
    unsigned int i = 0;
    int index = 0;

    ENTER();

    /* Init the wlan_private pointer array first */                                    首先初始化WLAN自身的指针数组 m_handle
    for (index = 0; index < MAX_MLAN_ADAPTER; index++) {             MAX_MLAN_ADAPTER默认值为2
        m_handle[index] = NULL;
    }
    /* Replace default fw image name for specific drv_mode */    把默认的固件镜像替换为指定的 drv_mode
    if (fw_name) {
        for (i = 0; i < (sizeof(drv_mode_tbl) / sizeof(drv_mode_tbl[0])); i++) {
            if (drv_mode_tbl[i].drv_mode == drv_mode) {
                drv_mode_tbl[i].fw_name = fw_name;
                break;
            }
        }
    }
    /* Init mutex */
    MOAL_INIT_SEMAPHORE(&AddRemoveCardSem);        初始化一个信号量作为互斥信号

    /* Register with bus */
    ret = woal_bus_register();                   把模块注册到总线上

    LEAVE();
    return ret;
}




/**
 *  @brief This function registers the IF module in bus driver                             把IF(不知道什么意思)注册到总线驱动上
 *  
 *  @return        MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
mlan_status
woal_bus_register(void)
{
    mlan_status ret = MLAN_STATUS_SUCCESS;

    ENTER();

    /* SDIO Driver Registration */
    if (sdio_register_driver(&wlan_sdio)) {                                                   把WLAN的sdio驱动结构链接到sdio驱动上。        
        PRINTM(MFATAL, "SDIO Driver Registration Failed \n");
        LEAVE();
        return MLAN_STATUS_FAILURE;
    }

    LEAVE();
    return ret;
}

在这分支:

一、wlan_sdio结构体分支下


static struct sdio_driver wlan_sdio = {      
    .name = "wlan_sdio",
    .id_table = wlan_ids,                   
    .probe = woal_sdio_probe,               
    .remove = woal_sdio_remove,              
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
    .drv = {
            .owner = THIS_MODULE,
#ifdef SDIO_SUSPEND_RESUME
#ifdef MMC_PM_KEEP_POWER
            .pm = &wlan_sdio_pm_ops,          
#endif
#endif
            }
#else
#ifdef SDIO_SUSPEND_RESUME
#ifdef MMC_PM_KEEP_POWER
    .drv = {
            .pm = &wlan_sdio_pm_ops,           
            }
#endif
#endif
#endif
};

注册一个sdio_driver类型的wlan_sdio设备

这里初始化的并不是全部的sdio_driver 结构体的成员


/*
 * SDIO function device driver
 */
struct sdio_driver {
        char *name;
        const struct sdio_device_id *id_table;

        int (*probe)(struct sdio_func *, const struct sdio_device_id *);
        void (*remove)(struct sdio_func *);

        struct device_driver drv;
};

这里有一个 device_driver类型的drv 结构体


struct device_driver {
        const char              *name;
        struct bus_type         *bus;

        struct module           *owner;
        const char              *mod_name;      /* used for built-in modules */

        bool suppress_bind_attrs;       /* disables bind/unbind via sysfs */

#if defined(CONFIG_OF)
        const struct of_device_id       *of_match_table;
#endif

        int (*probe) (struct device *dev);
        int (*remove) (struct device *dev);
        void (*shutdown) (struct device *dev);
        int (*suspend) (struct device *dev, pm_message_t state);
        int (*resume) (struct device *dev);
        const struct attribute_group **groups;

        const struct dev_pm_ops *pm;

        struct driver_private *p;
};



二、sdio_register_driver函数分支下


int sdio_register_driver(struct sdio_driver *drv)
{

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

drv->drv.name指的是sdio_driver下的drv结构体(也就是device_driver结构体)下的*name,而drv->name指的是wlan_sdio下的 .name="wlan_sdio"

        drv->drv.bus = &sdio_bus_type;

drv->drv.bus指的是sdio_driver下的drv结构体(也就是device_driver结构体)下的*bus,因为挂载在sdio总线上,所以类型直接赋值为 sdio_bus_type 。     

        return driver_register(&drv->drv);

注册设备wlan_sdio设备对应的设备驱动,并且寻找对应的设备并与之关联。

}  




.id_table = wlan_ids     --------->    const struct sdio_device_id *id_table;      ------------->

struct sdio_device_id {
        __u8    class;                  /* Standard interface or SDIO_ANY_ID */
        __u16   vendor;                 /* Vendor or SDIO_ANY_ID */
        __u16   device;                 /* Device ID or SDIO_ANY_ID */
        kernel_ulong_t driver_data      /* Data private to the driver */
                __attribute__((aligned(sizeof(kernel_ulong_t))));
};

一个 sdio_driver 会把它的这张id 表去和每一个sdio 设备的实际情况进行比较,如果该设备的实际情况和这张表里的某一个id 相同,准确地说,只有这许多特征都吻合,才能够把一个sdio device 和这个sdio driver 进行绑定,这些特征哪怕差一点也不行.就像我们每个人都是一道弧,都在不停寻找能让彼此嵌成完整的圆的另一道弧,事实却是,每个人对PI的理解不尽相同, 而圆心能否重合,或许只有痛过才知道.差之毫厘,失之交臂.


/**  @brief This function handles client driver probe.
 *  
 *  @param func    A pointer to sdio_func structure.
 *  @param id      A pointer to sdio_device_id structure.
 *  @return        MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
static int
woal_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) 将驱动程序和设备进行绑定
{
    int ret = MLAN_STATUS_SUCCESS;
    struct sdio_mmc_card *card = NULL;
    moal_handle *handle;

    ENTER();

    PRINTM(MINFO, "vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n",
           func->vendor, func->device, func->class, func->num);

    card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL);
    if (!card) {
        PRINTM(MFATAL, "Failed to allocate memory in probe function!\n");
        LEAVE();
        return -ENOMEM;
    }

    card->func = func;

#ifdef MMC_QUIRK_BLKSZ_FOR_BYTE_MODE
    /* The byte mode patch is available in kernel MMC driver which fixes one
       issue in MP-A transfer. bit1: use func->cur_blksize for byte mode */
    func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
    /* wait for chip fully wake up */
    if (!func->enable_timeout)
        func->enable_timeout = 200;
#endif
    sdio_claim_host(func);

 /*
    调用的是mmc_claim_host(func->card->host);后面注释。
    */

    ret = sdio_enable_func(func);
    if (ret) {
        sdio_disable_func(func);
        sdio_release_host(func);
        kfree(card);
        PRINTM(MFATAL, "sdio_enable_func() failed: ret=%d\n", ret);
        LEAVE();
        return -EIO;
    }
    sdio_release_host(func);
    if (NULL == (handle = woal_add_card(card))) {
        PRINTM(MERROR, "woal_add_card failed\n");
        kfree(card);
        sdio_claim_host(func);
        sdio_disable_func(func);
        sdio_release_host(func);
        ret = MLAN_STATUS_FAILURE;
    }

    LEAVE();
    return ret;
}




/** Structure: SDIO MMC card */                        
struct sdio_mmc_card
{
        /** sdio_func structure pointer */
    struct sdio_func *func;                                
        /** moal_handle structure pointer */
    moal_handle *handle;                                   
        /** saved host clock value */
    unsigned int host_clock;
};





/**
 *      sdio_claim_host - exclusively claim a bus for a certain SDIO function
 *      @func: SDIO function that will be accessed
 *
 *      Claim a bus for a set of operations. The SDIO function given
 *      is used to figure out which bus is relevant.
 */
void sdio_claim_host(struct sdio_func *func)                               
{
        BUG_ON(!func);
        BUG_ON(!func->card);                                                  

        mmc_claim_host(func->card->host);                          
}
EXPORT_SYMBOL_GPL(sdio_claim_host);          

 /*
    驱动中使用mmc_claim_host(host);来得知,当前mmc控制器是否被占用,当前mmc控制器如果被占用,那么 host->claimed = 1;否则为0,如果为1,那么会在for(;;)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行 mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他程序获得mmc主控制器的物理使用权
    */



/**
 *      sdio_enable_func - enables a SDIO function for usage
 *      @func: SDIO function to enable
 *
 *      Powers up and activates a SDIO function so that register
 *      access is possible.
 */
int sdio_enable_func(struct sdio_func *func)                      
{
        int ret;
        unsigned char reg;
        unsigned long timeout;

        BUG_ON(!func);
        BUG_ON(!func->card);

        pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func));

        ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg); 
        if (ret)
                goto err;

        reg |= 1 << func->num;

        ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
        if (ret)
                goto err;

        timeout = jiffies + msecs_to_jiffies(func->enable_timeout); 

        while (1) {
                ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, &reg);   
                if (ret)
                        goto err;
                if (reg & (1 << func->num))
                        break;
                ret = -ETIME;
                if (time_after(jiffies, timeout))               
                        goto err;
        }

        pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func));         

        return 0;

err:
        pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func)); 
        return ret;
}



mmc_io_rw_direct()把所有参数直接传递给mmc_io_rw_direct_host()SDIO功能部分简单了解下就可以,一般HOST部分芯片厂商都会做好。



/**
 * @brief This function adds the card. it will probe the
 *              card, allocate the mlan_private and initialize the device.
 *  
 *  @param card    A pointer to card
 *
 *  @return        A pointer to moal_handle structure
 */
moal_handle *
woal_add_card(void *card)          
{
    moal_handle *handle = NULL;
    mlan_status status = MLAN_STATUS_SUCCESS;
    int i;
    int netlink_num = NETLINK_MARVELL;
    int index = 0;

    ENTER();

    if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem))                 
        goto exit_sem_err;

    /* Allocate buffer for moal_handle */
    if (!(handle = kmalloc(sizeof(moal_handle), GFP_ATOMIC))) {
        PRINTM(MERROR, "Allocate buffer for moal_handle failed!\n");
        goto err_handle;
    }

    /* Init moal_handle */
    memset(handle, 0, sizeof(moal_handle));
    handle->card = card;
    /* Save the handle */
    for (index = 0; index < MAX_MLAN_ADAPTER; index++) {
        if (m_handle[index] == NULL)
            break;
    }
    if (index < MAX_MLAN_ADAPTER) {
        m_handle[index] = handle;
        handle->handle_idx = index;
    } else {
        PRINTM(MERROR, "Exceeded maximum cards supported!\n");
        goto err_kmalloc;
    }

    if (mac_addr) {
        t_u8 temp[20];
        t_u8 len = strlen(mac_addr) + 1;
        if (len < sizeof(temp)) {
            memcpy(temp, mac_addr, len);
            handle->set_mac_addr = 1;
            /* note: the following function overwrites the temp buffer */
            woal_mac2u8(handle->mac_addr, temp);               
        }
    }

    ((struct sdio_mmc_card *) card)->handle = handle;

    /* Init SW */
    if (MLAN_STATUS_SUCCESS != woal_init_sw(handle)) {           
        PRINTM(MFATAL, "Software Init Failed\n");
        goto err_kmalloc;
    }

    do {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
        handle->nl_sk =
            netlink_kernel_create(netlink_num, NL_MULTICAST_GROUP, NULL,      
                                  THIS_MODULE);
#else
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
        handle->nl_sk =
            netlink_kernel_create(netlink_num, NL_MULTICAST_GROUP, NULL, NULL,      
                                  THIS_MODULE);
#else
        handle->nl_sk =
            netlink_kernel_create(&init_net, netlink_num, NL_MULTICAST_GROUP,            
                                  NULL, NULL, THIS_MODULE);
#endif
#endif
        if (handle->nl_sk) {
            PRINTM(MINFO, "Netlink number = %d\n", netlink_num);
            handle->netlink_num = netlink_num;
            break;
        }
        netlink_num--;
    } while (netlink_num > 0);

    if (handle->nl_sk == NULL) {
        PRINTM(MERROR,
               "Could not initialize netlink event passing mechanism!\n");
        goto err_kmalloc;
    }

    /** Create workqueue */
    handle->workqueue = create_workqueue("MOAL_WORK_QUEUE");       
    if (!handle->workqueue)
        goto err_kmalloc;

    MLAN_INIT_WORK(&handle->main_work, woal_main_work_queue);       

#ifdef REASSOCIATION
    PRINTM(MINFO, "Starting re-association thread...\n");
    handle->reassoc_thread.handle = handle;
    woal_create_thread(woal_reassociation_thread,                    
                       &handle->reassoc_thread, "woal_reassoc_service");

    while (!handle->reassoc_thread.pid) {
        woal_sched_timeout(2);                                   
    }
#endif /* REASSOCIATION */

    /* Register the device. Fill up the private data structure with relevant
       information from the card and request for the required IRQ. */
    if (woal_register_dev(handle) != MLAN_STATUS_SUCCESS) {   
        PRINTM(MFATAL, "Failed to register wlan device!\n");
        goto err_registerdev;
    }

    /* Since settling time after runtime resume is huge (~10s) we
       can't really use runtime PM. Fortunatelly the firmware is quite
       good at conserving power when the device is not used. */
    pm_runtime_forbid(&((struct sdio_mmc_card *)card)->func->dev);       

    /* Init FW and HW */
    if (MLAN_STATUS_SUCCESS != woal_init_fw(handle)) {       
        PRINTM(MFATAL, "Firmware Init Failed\n");
        goto err_init_fw;
    }

#ifdef CONFIG_PROC_FS
    /* Initialize proc fs */
    woal_proc_init(handle);                                         
#endif /* CONFIG_PROC_FS */
    /* Add interfaces */
    for (i = 0; i < handle->drv_mode->intf_num; i++) {
        if (!woal_add_interface                                    
            (handle, handle->priv_num,
             handle->drv_mode->bss_attr[i].bss_type)) {
            status = MLAN_STATUS_FAILURE;
            break;
        }
        handle->priv_num++;
    }
    if (status != MLAN_STATUS_SUCCESS)
        goto err_add_intf;
    MOAL_REL_SEMAPHORE(&AddRemoveCardSem);                     
    LEAVE();
    return handle;
  err_add_intf:
    for (i = 0; i < handle->priv_num; i++)
        woal_remove_interface(handle, i);                        
#ifdef CONFIG_PROC_FS
    woal_proc_exit(handle);                                       
#endif
  err_init_fw:
    pm_runtime_allow(&((struct sdio_mmc_card *)card)->func->dev);        
    /* Unregister device */
    PRINTM(MINFO, "unregister device\n");
    woal_unregister_dev(handle);                                    
  err_registerdev:
    handle->surprise_removed = MTRUE;
    woal_terminate_workqueue(handle);                              
#ifdef REASSOCIATION
    if (handle->reassoc_thread.pid) {
        wake_up_interruptible(&handle->reassoc_thread.wait_q);        
    }
    /* waiting for main thread quit */
    while (handle->reassoc_thread.pid) {
        woal_sched_timeout(2);                     
    }
#endif /* REASSOCIATION */
  err_kmalloc:
    if ((handle->hardware_status == HardwareStatusFwReady) ||
        (handle->hardware_status == HardwareStatusReady)) {
        PRINTM(MINFO, "shutdown mlan\n");
        handle->init_wait_q_woken = MFALSE;
        status = mlan_shutdown_fw(handle->pmlan_adapter);         
        if (status == MLAN_STATUS_PENDING)
            wait_event_interruptible(handle->init_wait_q,       
                                     handle->init_wait_q_woken);
    }
    woal_free_moal_handle(handle);                           
    if (index < MAX_MLAN_ADAPTER) {
        m_handle[index] = NULL;
    }
    ((struct sdio_mmc_card *) card)->handle = NULL;
  err_handle:
    MOAL_REL_SEMAPHORE(&AddRemoveCardSem); 
  exit_sem_err:
    LEAVE();
    return NULL;
}




原创粉丝点击