linux内核 初始化 wifi,基于linux的SDIO/wifi驱动分析

本文解析了SDIO总线接口及其在Linux系统中的实现,包括SDIO总线的信号特性、热插拔原理和命令格式等内容。进一步介绍了SDIO接口层与WiFi驱动的交互过程,涵盖sdio_driver注册、匹配机制、数据发送接收流程及设备移除处理等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、sdio接口层解析

SDIO总线

SDIO总线 和 USB总线

类似,SDIO也有两端,其中一端是HOST端,另一端是device端。所有的

通信 都是 由HOST端 发送 命令

开始的,Device端只要能解析命令,就可以相互通信。

CLK信号:HOST给DEVICE的时钟信号,每个时钟周期传输一个命令。

CMD信号:双向 的信号,用于传送 命令

和 反应。

DAT0-DAT3 信号:四条用于传送的数据线。

VDD信号:电源信号。

VSS1,VSS2:电源地信号。

SDIO热插拔原理:

方法:设置一个 定时器检查 或 插拔中断检测

硬件:假如GPG10(EINT18)用于SD卡检测

GPG10 为高电平 即没有插入SD卡

GPG10为低电平  即插入了SD卡

SDIO命令

SDIO总线上都是HOST端发起请求,然后DEVICE端回应请求。sdio命令由6个字节组成。

1. Command:用于开始传输的命令,是由HOST端发往DEVICE端的。其中命令是通过CMD信号线传送的。

2. Response:回应是DEVICE返回的HOST的命令,作为Command的回应。也是通过CMD线传送的。

3. Data:数据是双向的传送的。可以设置为1线模式,也可以设置为4线模式。数据是通过DAT0-DAT3信号线传输的。

SDIO的每次操作都是由HOST在CMD线上发起一个CMD,对于有的CMD,DEVICE需要返回Response,有的则不需要。

对于读命令,首先HOST会向DEVICE发送命令,紧接着DEVICE会返回一个握手信号,此时,当HOST收到回应的握手信号后,会将数据放在4位的数据线上,在传送数据的同时会跟随着CRC校验码。当整个读传送完毕后,HOST会再次发送一个命令,通知DEVICE操作完毕,DEVICE同时会返回一个响应。

对于写命令,首先HOST会向DEVICE发送命令,紧接着DEVICE会返回一个握手信号,此时,当HOST收到回应的握手信号后,会将数据放在4位的数据线上,在传送数据的同时会跟随着CRC校验码。当整个写传送完毕后,HOST会再次发送一个命令,通知DEVICE操作完毕,DEVICE同时会返回一个响应。

sd命令格式

以IO_SEND_OP_COND命令为例包含以下部分:

S(开始位) 总为0

D(方向位) 1 从host到 device (0

从device到host)

命令索引:  通过值000101B来

填充位

0

IO_OCR

运转条件寄存器所支持的VDD的最小值和最大值

CRC7

7位CRC校验数据

E(结束位)  总为1

MMC命令总共有40多个,分为class0

~class7共8类,class0的所有卡必须支持。驱动程序通过发送cmd1、cmd41来区分sd卡和mmc卡,如果发送cmd1返回成功,则为mmc卡,否则发送cmd41返回成功,则为sd卡。

cmd0   初始化mmc卡

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

Sdio接口驱动

首先我们来探讨几个重要的数据结构:该结果位于core核心层,主要用于 核心层与主机驱动层

的数据交换处理。/include/linux/mmc/host.h

struct

mmc_host 用来描述 卡控制器

struct

mmc_card 用来描述 卡

struct

mmc_driver 用来描述 mmc 卡驱动

struct sdio_func

用来描述 功能设备

struct mmc_host_ops 用来描述 卡控制器操作接口函数功能,用于从

主机控制器层向 core 层注册操作函数,从而将core 层与具体的主机控制器隔离。也就是说 core 要操作主机控制器,就用这个 ops 当中给的函数指针操作,不能直接调用具体主控制器的函数。

编写Host层驱动,可以参考S3C24XX的HOST驱动程序/drivers/mmc/host/s3cmci.c

static struct platform_driver s3cmci_driver = {

.driver  = {

.name    =

"s3c-sdi",  //名称和平台设备定义中的对应

.owner   = THIS_MODULE,

.pm  = s3cmci_pm_ops,

},

.id_table = s3cmci_driver_ids,

.probe

= s3cmci_probe,  //平台设备探测接口函数

.remove

= __devexit_p(s3cmci_remove),

.shutdown = s3cmci_shutdown,

};

s3cmci_probe(struct platform_device *pdev)

{

//....

struct mmc_host *mmc;

mmc = mmc_alloc_host(sizeof(struct s3cmci_host),

&pdev->dev);

//分配mmc_host结构体

//.....

}

request_irq(host->irq, s3cmci_irq, 0,

DRIVER_NAME, host) //注册中断处理函数s3cmci_irq

request_irq(host->irq_cd,

s3cmci_irq_cd,IRQF_TRIGGER_RISING

|IRQF_TRIGGER_FALLING, DRIVER_NAME, host)

mmc_add_host(mmc);  //initialise host hardware

//向MMC core注册host驱动

---->

device_add(&host->class_dev);

//添加设备到mmc_bus_type总线上的设备链表中

----> mmc_start_host(host); //启动mmc host

---->mmc_detect_change(host,

0);

mmc_schedule_delayed_work(&host->detect,

delay);

搜索host->detected得到以下信息:

/drivers/mmc/core/host.c

INIT_DELAYED_WORK(&host->detect,

mmc_rescan);

mmc_rescan(struct work_struct *work)

---->mmc_bus_put(host);//card

从bus上移除时,释放它占有的总线空间

mmc_claim_host(host);

mmc_rescan_try_freq(host, max(freqs[i],

host->f_min);

static int mmc_rescan_try_freq(struct mmc_host *host, unsigned

freq)

{

…………………………………………..

if (!mmc_attach_sdio(host))

return 0;

if (!mmc_attach_sd(host))

return 0;

if (!mmc_attach_mmc(host))

return 0;

………………………………………….

}

mmc_attach_sdio(struct mmc_host *host)

//匹配sdio接口卡

--->mmc_attach_bus(host,

&mmc_sdio_ops);

mmc_sdio_init_card(host, host->ocr, NULL,

0);

--->card = mmc_alloc_card(host,

NULL);//分配一个card结构体

mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); //设置mmc_bus的工作模式

struct sdio_func

*sdio_func[SDIO_MAX_FUNCS]; //SDIO functions (devices)

sdio_init_func(host->card, i + 1);

--->func = sdio_alloc_func(card); //分配struct

sdio_fun(sdio功能设备)结构体

mmc_io_rw_direct();

card->sdio_func[fn - 1] = func;

mmc_add_card(host->card);

//将具体的sdio设备挂载到mmc_bus_types 总线

sdio_add_func(host->card->sdio_func[i]);

//将sdio功能设备挂载到sdio_bus_types总线

附:mmc_card结构体中sdio_func[]赋值为分配的sdio_fun结构体,通过fun结构体与wireless驱动层建立联系

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

mmc_attach_sd(struct mmc_host *host)

//匹配sd卡

---> mmc_sd_attach_bus_ops(host);

mmc_sd_init_card(host, host->ocr,

NULL);//检测、初始化sd卡

mmc_add_card(struct mmc_card *card) // Register a new MMC card

with the driver model.以驱动模式注册一个新mmc卡

mmc_claim_host(host);

mmc_attach_mmc(struct mmc_host *host)

//匹配mmc卡

---> mmc_attach_bus_ops(host);

mmc_init_card(host, host->ocr, NULL);//检测、初始化mmc

mmc_add_card(host->card);

mmc_claim_host(host);

struct mmc_request {

struct mmc_command *cmd;

struct

mmc_data

*data;

struct mmc_command *stop;

void *done_data;

void (*done)(struct mmc_request *);

};

#define writel(val, addr) outl((val), (unsigned long)(addr))

#define readl(addr) inl((unsigned long)(addr))

s3cmci_send_request(struct mmc_host *mmc)

---->struct mmc_request *mrq =

host->mrq;

清除相应状态寄存器

writel(0xFFFFFFFF, host->base +

S3C2410_SDICMDSTAT);

writel(0xFFFFFFFF, host->base +

S3C2410_SDIDSTA);

writel(0xFFFFFFFF, host->base +

S3C2410_SDIFSTA);

---->s3cmci_setup_data(host,

cmd->data);

writel(0, host->base + S3C2410_SDIDCON);

//写SDIDCON寄存器

添加相应中断

imsk =

S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |

S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;

enable_imask(host,

imsk);  //使能中断

writel(0x0000FFFF,

host->base + S3C2410_SDITIMER);

//设置sditimer寄存器的状态

writel(0xFF, host->base +

S3C2410_SDIPRE);

//设置sdipre SDI预分频寄存器的状态

---->if

(s3cmci_host_usedma(host))

//判断host的传送模式:dma(直接内存访问模式) 或 pio(CPU执行I/O端口指令来进行数据的读写的数据交换模式)

res = s3cmci_prepare_dma(host, cmd->data);

else

res = s3cmci_prepare_pio(host, cmd->data);

---->s3cmci_prepare_dma(struct s3cmci_host *host,

struct mmc_data *data)

s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM :

S3C2410_DMASRC_HW); //设置dma模式的属性

s3c2410_dma_ctrl(unsigned int channel, enum s3c2410_chan_op

op)

//对dma传输模式的进行相关控制函数

s3c2410_dma_start(chan);

//开始

s3c2410_dma_dostop(chan);

//停止

s3c2410_dma_flush(chan);

//刷新

s3c2410_dma_started(chan);

---->s3cmci_prepare_pio(host,

cmd->data);

do_pio_write(host);

enable_imask(host,

S3C2410_SDIIMSK_TXFIFOHALF);  //使能Tx FIFO half

interrupt 中断

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

s3cmci_send_command(struct s3cmci_host *host,struct mmc_command

*cmd)

---->writel(cmd->arg,

host->base + S3C2410_SDICMDARG);

//将命令写到sdicarg寄存器中

writel(ccon, host->base +

S3C2410_SDICMDCON);

//将ccon写到sdiccon(sdi控制寄存器)中

s3cmci_irq(int irq, void *dev_id)

//设置sdi传输的相关中断处理

---->mmc_signal_sdio_irq(host->mmc);

//sdio 中断信号

wake_up_process(host->sdio_irq_thread);

//唤醒

当插拔SDIO设备,会触发中断通知到CPU,然后执行卡检测中断处理函数在这个中断服务函数中,mmc_detect_change->mmc_schedule_delayed_work(&host->detect,delay),

INIT_DELAYED_WORK(&host->detect,

mmc_rescan)会调度mmc_rescan函数延时调度工作队列,这样也会触发SDIO设备的初始化流程,检测到有效的SDIO设备后,会将它注册到系统中去。

static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)

{

struct s3cmci_host *host = (struct s3cmci_host *)dev_id;

........

mmc_detect_change(host->mmc,

msecs_to_jiffies(500));

return IRQ_HANDLED;

}

2、wifi驱动解析

Drivers/net/wireless/libertas/if_sdio.c

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

struct sdio_driver {

char *name;  //设备名

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

int (*probe)(struct sdio_func *, const struct sdio_device_id

*);//匹配函数

void (*remove)(struct sdio_func *);

struct device_driver drv;

};

static struct sdio_driver if_sdio_driver = {

.name

= "libertas_sdio",

.id_table = if_sdio_ids,  //用于设备与驱动的匹配

.probe

= if_sdio_probe,

.remove

= if_sdio_remove,

.drv = {

.pm = &if_sdio_pm_ops,

},

};

int sdio_register_driver(struct sdio_driver *drv)

{

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

drv->drv.bus =

&sdio_bus_type;

//设置driver的bus为sdio_bus_type

return

driver_register(&drv->drv);

}

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,

};

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

static int sdio_bus_match(struct device *dev, struct

device_driver *drv)

{

struct sdio_func *func = dev_to_sdio_func(dev);

struct sdio_driver *sdrv = to_sdio_driver(drv);

if (sdio_match_device(func, sdrv))

return 1;

return 0;

}

static const struct sdio_device_id *sdio_match_device(struct

sdio_func *func,

struct sdio_driver *sdrv)

{

const struct sdio_device_id *ids;

ids = sdrv->id_table;

if (sdio_match_one(func, ids))

return ids;

}

由以上匹配过程来看,通过匹配id_table 和

sdio_driver设备驱动中id,来匹配合适的驱动或设备。最终会调用.probe函数,来完成相关操作。

If_sdio_probe函数分析

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

net_device_ops

sdio_register_driver(&if_sdio_driver);

//注册sdio_driver结构体

card->workqueue =

create_workqueue("libertas_sdio");  //创建工作队列

INIT_WORK(&card->packet_worker,

if_sdio_host_to_card_worker);

card->model == fw_table[i].model

//检测是否支持wifi卡

sdio_claim_host(func);

ret = sdio_enable_func(func); // enables a SDIO function for

usage

使能sdio功能设备

ret = sdio_claim_irq(func, if_sdio_interrupt);

priv = lbs_add_card(card,

&func->dev);

----> wdev = lbs_cfg_alloc(dmdev); //

分配一个无线网络设备结构体,并初始化

lbs_init_adapter(priv) // initialize adapter

structure 初始化网络适配器

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

//分配一个net_device结构体,并对其成员赋值

SET_NETDEV_DEV(dev, dmdev);

wdev->netdev = dev;

priv->dev = dev;

dev->netdev_ops =

&lbs_netdev_ops;

init_waitqueue_head(&priv->waitq);

//初始化等待队列头

priv->main_thread = kthread_run(lbs_thread, dev,

"lbs_main");//创建内核线程lbs_thread,它的重要功能为:It handles all events

generated by firmware, RX data received from firmware and TX data

sent from kernel.即通过固件产生收发事件,从固件接受数据包,发送数据包给kernel。

ret = lbs_start_card(priv);

---->ret = lbs_setup_firmware(priv);// determined

the firmware capabities.

设置fireware固件的功能

lbs_cfg_register(priv)

----> ret =

register_netdev(priv->dev);//注册net_device网络设备

priv->card = card;

priv->hw_host_to_card =

if_sdio_host_to_card;  //发送数据

priv->enter_deep_sleep =

if_sdio_enter_deep_sleep;

priv->exit_deep_sleep = if_sdio_exit_deep_sleep;

priv->reset_deep_sleep_wakeup =

if_sdio_reset_deep_sleep_wakeup;

发送数据包

if_sdio_host_to_card(struct lbs_private *priv,u8 type, u8 *buf,

u16 nb)

----> packet = kzalloc(sizeof(struct

if_sdio_packet) + size,GFP_ATOMIC);

struct if_sdio_packet {

struct if_sdio_packet  *next;

u16

nb;

u8

buffer[0] __attribute__((aligned(4)));

//缓冲区为4字节对齐

};

memcpy(packet->buffer + 4, buf,

nb);  //复制buf的数据到packet

queue_work(card->workqueue,

&card->packet_worker);

static const struct net_device_ops lbs_netdev_ops = {

.ndo_open

= lbs_dev_open,

.ndo_stop

= lbs_eth_stop,

.ndo_start_xmit

= lbs_hard_start_xmit, //数据包发送函数

.ndo_set_mac_address   =

lbs_set_mac_address,

.ndo_tx_timeout    =

lbs_tx_timeout,

.ndo_set_multicast_list = lbs_set_multicast_list,

.ndo_change_mtu

= eth_change_mtu,

.ndo_validate_addr = eth_validate_addr,

};

lbs_hard_start_xmit()//检测发送条件 和 启动数据包的发送

---->

netif_stop_queue(priv->mesh_dev);

//当发送队列为满或因其他原因来不及发送当前上层传送下来的数据包,则调用此函数阻止上层继续向网络设备驱动传递数据包。当数据包被发送完成后Tx结束的中断处理中,应该调用netif_wake_queue(priv->mesh_dev);唤醒被阻塞的上层,以启动继续向网络设备驱动传送数据包。

netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct

net_device *dev)

{

……………………………………

netif_stop_queue(priv->dev);

if (priv->tx_pending_len) {

lbs_deb_tx("Packet on %s while busy\n",

dev->name);

ret = NETDEV_TX_BUSY;

goto unlock;

}

…………………………….

txpd = (void

*)priv->tx_pending_buf;

//将发送buf的地址赋给txpd

memcpy(&txpd[1], p802x_hdr,

le16_to_cpu(txpd->tx_packet_length));

unlock:

wake_up(&priv->waitq);

//唤醒等待队列

}

由以上函数得知,当发送队列为满或因其他原因来不及发送当前上层传递下来的数据包,就会调用netif_stop_queue阻止上层继续向网络设备驱动发送数据包。priv->tx_pending_len为skb中要发送数据的长度,当它不为0时,就会调用wake_up(&priv->waitq)来唤醒等待队列头&priv->waitq。

搜索priv->waitq得知,priv->waitq等待队列头(drivers\net\wireless\libertas\main.c)

init_waitqueue_head(&priv->waitq);

priv->main_thread = kthread_run(lbs_thread, dev,

"lbs_main"); //创建内核线程lbs_thread

lbs_thread(void *data)  //kernel thread

---->add_wait_queue(&priv->waitq,

&wait);  //将等待队列添加到wait等待队列中

if (!priv->dnld_sent

&&

!priv->cur_cmd)

lbs_execute_next_command(priv);

priv->hw_host_to_card(priv,

MVMS_DAT,priv->tx_pending_buf,priv->tx_pending_len);

注意:hw_host_to_card函数将带txpd头的packet通过sdio接口发送到wifi芯片。搜索priv->hw_host_to_card得到drivers\net\wireless\libertas\If_sdio.c中的

priv->hw_host_to_card =

if_sdio_host_to_card;

if_sdio_host_to_card(struct lbs_private *priv,u8 type, u8 *buf,

u16 nb)

----> packet = kzalloc(sizeof(struct

if_sdio_packet) + size,GFP_ATOMIC); //分配套接字缓冲区

memcpy(packet->buffer + 4, buf,

nb); //复制数据到网络缓冲区(skb)

queue_work(card->workqueue,

&card->packet_worker);

//工作队列

搜索card->packet_worker得到:card->workqueue

= create_workqueue("libertas_sdio");

INIT_WORK(&card->packet_worker,

if_sdio_host_to_card_worker);

if_sdio_host_to_card_worker(struct work_struct *work)

----> sdio_writesb(card->func,

card->ioport,packet->buffer,

packet->nb);

//将io端口、sk_buff的数据包信息写到card->func中。

----> sdio_io_rw_ext_helper(func,

1, addr, 0, src, count); //function: Split an arbitrarily sized

data transfer into several IO_RW_EXTENDED commands.

一个任意大小的数据传输分裂成几IO_RW_EXTENDED命令。

---->

mmc_io_rw_extended(func->card,

write,func->num, addr, incr_addr, buf,blocks,

func->cur_blksize);

mmc_io_rw_extended(struct mmc_card *card, int write, unsigned

fn,unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned

blksz)

----> struct mmc_request mrq;

struct mmc_command cmd;

struct mmc_data data;

mmc_wait_for_req(card->host,

&mrq); //mmc_host等待响应请求

----> mmc_start_request(host,

mrq); //开始执行请求

---->

host->ops->request(host, mrq);

wait_for_completion(&complete);

//等待请求的完成

注意:struct mmc_host *host,struct mmc_host_ops *ops;

void  (*request)(struct mmc_host *host, struct

mmc_request

*req);因为请求已经通过sdio接口发送到card上,所以我们可以联想到s3cmci.c函数中的struct

mmc_host_ops s3cmci_ops

结构体中.request

= s3cmci_request成员。

static void s3cmci_request(struct mmc_host *mmc, struct

mmc_request *mrq)

{

struct s3cmci_host *host = mmc_priv(mmc);

host->status = "mmc request";

host->cmd_is_stop = 0;

host->mrq = mrq;

if (

s3cmci_card_present(mmc)

== 0) {

dbg(host, dbg_err, "%s: no medium present\n", __func__);

host->mrq->cmd->error

= -ENOMEDIUM;

mmc_request_done(mmc, mrq);  //若卡不存在,马上结束请求

} else

s3cmci_send_request(mmc);

}

由函数得知,s3cmci_card_present(mmc)判断卡是否存在,若存在则发送命令请求。

s3cmci_send_request(struct mmc_host *mmc)

清空sdi命令状态寄存器、数据状态寄存器和fifo状态寄存器。

---->writel(0xFFFFFFFF, host->base

+ S3C2410_SDICMDSTAT);

writel(0xFFFFFFFF, host->base +

S3C2410_SDIDSTA);

writel(0xFFFFFFFF, host->base +

S3C2410_SDIFSTA);

s3cmci_setup_data(host,

cmd->data);

//进行数据请求处理设置,主要是数据控制寄存器

----> writel(dcon, host->base +

S3C2410_SDIDCON);//将dcon写到sdidcon 数据控制寄存器中

s3cmci_host_usedma(host)

//判断发送模式是dma还是pri

mmc_request_done(mmc, mrq); //请求处理完成

s3cmci_send_command(host, cmd);

----> writel(cmd->arg,

host->base + S3C2410_SDICMDARG); //写sdi command

argument register寄存器

writel(ccon, host->base +

S3C2410_SDICMDCON);  //写sdicmdcon命令控制寄存器

数据包接收

网络设备接收数据的主要方法是由中断引发设备的中断处理函数,中断处理函数判断中断的类型,如果为接收中断,则读取接收到的数据,分配sk_buff数据结构和数据缓冲区,并将接收的数据复制到数据缓存区,并调用netif_rx()函数将sk_buff传递给上层协议。

搜索if_sdio_interrupt,可知道它是在if_sdio.c文件中if_sdio_probe()函数中sdio_claim_irq(func,

if_sdio_interrupt) ,func->irq_handler =

if_sdio_interrupt。当s3cmci_irq中断处理函数的S3C2410_SDIIMSK_SDIOIRQ

中断被触发时将调用if_sdio_interrupt()函数,进行接收数据。

#define

S3C2410_SDIIMSK_SDIOIRQ

(1<<12)

由s3c2410芯片手册,sd host receives SDIO interrupter from the card (for

SDIO) ,0 = disable 1 = interrupt enable.

s3cmci_irq()

---->

mmc_signal_sdio_irq(host->mmc);

//中断信号

---->

wake_up_process(host->sdio_irq_thread);

//唤醒线程sdio_irq_thread

搜索sdio_irq_thread可知,Sdio_irq.c (drivers\mmc\core)

host->sdio_irq_thread =

kthread_run(sdio_irq_thread, host,

"ksdioirqd/%s",mmc_hostname(host));

sdio_irq_thread()线程处理函数

---->

process_sdio_pending_irqs(host->card); //挂起中断

----> if

(func->irq_handler)

{func->irq_handler(func);}

if_sdio_interrupt(struct sdio_func *func) //中断处理函数

---->

if_sdio_card_to_host(card);  //host

控制器从card接收数据

static int if_sdio_card_to_host(struct if_sdio_card *card)

{

size = if_sdio_read_rx_len(card, &ret);

ret = if_sdio_wait_status(card, IF_SDIO_IO_RDY);

type = card->buffer[2] |

(card->buffer[3] <<

8);

………………………………………………………………

switch (type) {

case MVMS_CMD:

ret = if_sdio_handle_cmd(card, card->buffer + 4,

chunk - 4);

case MVMS_DAT:

ret = if_sdio_handle_data(card, card->buffer + 4,

chunk - 4);

case MVMS_EVENT:

ret = if_sdio_handle_event(card, card->buffer + 4,

chunk - 4);

}

…………………………………………………….

}

该函数主要完成向上层上交命令、数据、事件。

if_sdio_handle_data(struct if_sdio_card *card,u8 *buffer,

unsigned size)

----> skb =

dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN);

分配sk_buff套接字缓冲区

lbs_process_rxed_packet(card->priv,

skb);

----> skb->protocol

= eth_type_trans(skb, dev); //获取网络协议类型

netif_rx(skb); //根据网络协议类型将数据包交给上层处理

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

当sdio卡拔除时,驱动会调用该函数,完成相应操作。如释放占有的资源,禁止func功能函数,释放host。

if_sdio_remove(struct sdio_func *func)

---->lbs_stop_card(card->priv);

lbs_remove_card(card->priv);

---->kthread_stop(priv->main_thread);

//终止内核线程

lbs_free_adapter(priv);

lbs_cfg_free(priv);

free_netdev(dev);

flush_workqueue(card->workqueue);

//刷新工作队列

destroy_workqueue(card->workqueue);

sdio_claim_host(func);

sdio_release_irq(func);

sdio_disable_func(func);

sdio_release_host(func);

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值