一、sdio接口層解析linux
SDIO總線算法
SDIO總線和USB總線相似,SDIO也有兩端,其中一端是HOST端,另外一端是device端。全部的通訊都是由HOST端發送命令開始的,Device端只要能解析命令,就能夠相互通訊。緩存
CLK信號:HOST給DEVICE的時鐘信號.每一個時鐘週期傳輸一個命令或數據位。網絡
CMD信號:雙向的信號,用於傳送命令和反應。數據結構
DAT0-DAT3 信號:四條用於傳送的數據線。app
VDD信號:電源信號。less
VSS1,VSS2:電源地信號。函數
nCD 用於檢測卡是否插入。this
fs2410 mmc/sd的電路圖spa
SDIO熱插拔原理:
方法:設置一個定時器檢查或插拔中斷檢測
硬件:GPG10(EINT18)用於SD卡檢測
GPG10 爲高電平 即沒有插入SD卡(參見fs2410原理圖/芯片手冊)
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的全部卡必須支持。驅動程序經過發送cmd一、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 當中給的函數指針操做,不能直接調用具體主控制器的函數。
/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);
mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev); //分配mmc_host結構體
---->INIT_DELAYED_WORK(&host->detect, mmc_rescan); //初始化工做隊列
/*註冊中斷處理函數s3cmci_irq,來處理數據收發過程引發的各類中斷*/
request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host) //註冊中斷處理函數s3cmci_irq
/*註冊中斷處理s3cmci_irq_cd函數,來處理熱撥插引發的中斷,中斷觸發的形式爲上升沿、降低沿觸發*/
request_irq(host->irq_cd, s3cmci_irq_cd,IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING,
DRIVER_NAME, host)
mmc_add_host(mmc); //initialise host hardware 初始化host控制器
----> device_add(&host->class_dev); //添加設備到mmc_bus_type總線上的設備鏈表中
----> mmc_start_host(host); //啓動mmc host
/*MMC drivers should call this when they detect a card has been inserted or removed.檢測sd卡是否插上或移除*/
---->mmc_detect_change(host, 0);
/*Schedule delayed work in the MMC work queue.調度延時工做隊列*/
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 host控制器是否被佔用,當前mmc控制器若是被佔用,那麼 host->claimed = 1;不然爲0
*若是爲1,那麼會在while(1)循環中調用schedule切換出本身,當佔用mmc控制器的操做完成以後,執行 *mmc_release_host()的時候,會激活登記到等待隊列&host->wq中的其餘程序得到mmc主控制器的使用權
*/
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)
{
…………………………………………..
/* Order's important: probe SDIO, then SD, then MMC */
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);
/*當card與總線上的驅動匹配,就初始化card*/
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; /* completion data */
void (*done)(struct mmc_request *);/* completion function */
};
/*將val寫到addr地址*/
#define writel(val, addr) outl((val), (unsigned long)(addr))
/*從addr地址處,獲取信息*/
#define readl(addr) inl((unsigned long)(addr))
/*發送請求*/
s3cmci_send_request(struct mmc_host *mmc)
---->struct mmc_request *mrq = host->mrq;
/* Clear command, data and fifo status registers
*Fifo clear only necessary on 2440, but doesn't hurt on 2410
*/清除相應狀態寄存器
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寄存器
/* add to IMASK register */ 添加相應中斷
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 中斷
---------------------------------------------------------------------------------------
/*host控制器發送命令*/
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;
}
二、wifi驅動解析
Drivers/net/wireless/libertas/if_sdio.c
Sdio設備的驅動由sdio_driver結構體定義,sdio_register_driver函數將該設備驅動掛載到sdio_bus_type總線上。
/* SDIO function device driver*/
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;
};
/*if_sdio.c*/
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,
},
};
/**
* sdio_register_driver - register a function driver
* @drv: SDIO function driver
*/
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功能設備
/* claim the IRQ for a SDIO function 註冊中斷*/
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);
/* gets the HW spec from the firmware and sets some basic parameters.*/
---->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) {
/* This can happen if packets come in on the mesh and eth
device simultaneously -- there's no mutual exclusion on
hard_start_xmit() calls between devices. */
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
/*將要發送的數據拷貝到priv->tx_pending_buf緩衝區*/
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等待隊列中
/* Execute the next command */
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)
/* Clear command, data and fifo status registersFifo clear only necessary on 2440, but doesn't hurt on 2410*/清空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);
----------------------------------------------------------------------------