s5p4418-sdio接口的wifi模块驱动及往android层的延伸

平台说明:

s5p4418

sdio接口的wifi模块驱动:If_sdio.c (drivers\net\wireless\libertas)  

linux 版本:3.4.29

MMC驱动子系统回顾:

在博客:http://blog.csdn.net/eliot_shao/article/details/50178475 中通过跟踪代码的方式,大概了解了mmc控制器HOST的驱动:建立host mmc设备,等待卡的插入,如果有卡插入,卡的类型包括SDIO接口wifi模块,emmc,tf,sd卡等,则控制器驱动判断设备类型是SDIO,MMC还是SD,如下:

  1. /* Order's important: probe SDIO, then SD, then MMC */  

  2.     if (!mmc_attach_sdio(host))  

  3.         return 0;  

  4.     if (!mmc_attach_sd(host))  

  5.         return 0;  

  6.     if (!mmc_attach_mmc(host))  

  7.         return 0;

其结果就是创建不同类型的设备,如果是SDIO则创建的设备struct sdio_func,如下:

  1.    struct sdio_func *func;  
  2.     func = sdio_alloc_func(card);  
  3. ----------------------------------------  
  4. /* 
  5.  * Allocate and initialise a new SDIO function structure. 
  6.  */  
  7. struct sdio_func *sdio_alloc_func(struct mmc_card *card)  
  8. {  
  9.     struct sdio_func *func;  
  10.   
  11.     func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);  
  12.     if (!func)  
  13.         return ERR_PTR(-ENOMEM);  
  14.   
  15.     func->card = card;  
  16.   
  17.     device_initialize(&func->dev);  
  18.   
  19.     func->dev.parent = &card->dev;  
  20.     func->dev.bus = &sdio_bus_type;  
  21.     func->dev.release = sdio_release_func;  
  22.   
  23.     return func;  

err = sdio_add_func(host->card->sdio_func[i]);
SDIO设备建立以后,sdio总线会遍历所有挂在总线上的驱动,然后执行,总线的probe函数,如下:

static struct 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,
};

---------

    sdio_match_device(func, drv);

----------------

if (ids) {
        while (ids->class || ids->vendor || ids->device) {
            if (sdio_match_one(func, ids))
                return ids;
            ids++;
        }
    }

-----------------------

static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
    const struct sdio_device_id *id)
{
    if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
        return NULL;
    if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
        return NULL;
    if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
        return NULL;
    return id;
}

-------------

最终通过驱动和设备struct sdio_device_id进行匹配!如果找到驱动,则会执行驱动的probe,进行一系列设备的初始化。

sdio接口的wifi模块驱动分析:

SDIO接口的wifi的通信接口也是mmc系统,所以wifi驱动也要加上一层mmc驱动的外衣。

这里主要分析:If_sdio.c (drivers\net\wireless\libertas)  

首先是sdio注册驱动:

sdio_register_driver(&if_sdio_driver);

假设现在总线已经找到了设备对应的驱动,先面就是执行static int if_sdio_probe

probe关于wifi模块的操作主要是:

1、调用函数alloc_netdev()函数分配网络设备。
2、对网络设备dev进行初始化,操作函数绑定之后,接着调用了register_netdev(dev)函数,将网络设备注入到内核。
到这里可以在系统跑起来后用ifconfig命令来查看这个网络设备,还可以设置这个网络设备的ip地址,网关,子网掩码等。
在这个probe函数中还创建了一个内核线程,用来管理这个网络设备的数据发送、事件的处理(卡的拔出)和一些命令的处理。还申请了一个接收数据中断,只要这个wifi模块接收到数据,就会发送一个中断,告诉sdi host我接收到了数据,然后,host就会发送读取命令,对其进行读取,将读取到的数据放到skbuf中递交给ip层。
数据接收: 
if_sdio_probe函数中我们已经申请了一个接收数据的中断
if_sdio_interrupt(),当接收到数据,wifi模块通过产生中断来提醒host去读取数据,host通过发送cmd53命令将数据接收后放到skbuf中,然后调用ip层提供的接口(netif_rx())函数将数据递交到ip层。
具体源码跟踪:

priv = lbs_add_card(card, &func->dev);-->

/* Allocate an Ethernet device and register it */
    wdev = lbs_cfg_alloc(dmdev);

if (lbs_init_adapter(priv)) {
        pr_err("failed to initialize adapter structure\n");
        goto err_wdev;
    }

dev = alloc_netdev(0, "wlan%d", ether_setup);

register_netdev(dev);

ret = if_sdio_power_on(card);-->

ret = sdio_claim_irq(func, if_sdio_interrupt);

if (cause & IF_SDIO_H_INT_UPLD) {
        ret = if_sdio_card_to_host(card);
        if (ret)
            goto out;
    }

case MVMS_DAT:
        ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4);
        if (ret)
            goto out;
        break;

skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN);

lbs_process_rxed_packet(card->priv, skb);

if (in_interrupt())
        netif_rx(skb);

发送数据的过程:
ip层通过接口(dev_queue_xmit())将数据递交给网络设备协议接口层,网络设备借口层利用netdevice中的注册函数的数据发送函数(.ndo_start_xmit = lbs_hard_start_xmit,),唤醒主线程priv->main_thread,在主线程中回调(priv->hw_host_to_card = if_sdio_host_to_card;)if_sdio_host_to_card()函数。紧接着唤醒packet_work(包处理队列),最后通过sdio协议将数据发送到wifi模块的sram中,wifi模块会自动的将对数据进行处理(例如DA转换等)同无线电波的形式将数据发送到无线路由器。


android的wifi延伸:


s5p4418-sdio接口的wifi模块驱动及往android层的延伸_第1张图片

你可能感兴趣的:(android系统开发,wifi,sdio)