Linux 2.6.38
sdio總線上driver和設備的match成功只是軟件之間的”切口“,好比《還錢》:
劉金山:明月幾時有?
馮鞏:擡頭自己瞅。
但是馮鞏並沒有馬上把錢請給他吧?這裏硬件的連通性、能不能工作還不知道,所以要probe探測一下。
static int sdio_bus_probe(struct device *dev)
{
struct sdio_driver *drv = to_sdio_driver(dev->driver);
struct sdio_func *func = dev_to_sdio_func(dev);
const struct sdio_device_id *id;
int ret;
id = sdio_match_device(func, drv);
if (!id)
return -ENODEV;
/* Unbound SDIO functions are always suspended.
* During probe, the function is set active and the usage count
* is incremented. If the driver supports runtime PM,
* it should call pm_runtime_put_noidle() in its probe routine and
* pm_runtime_get_noresume() in its remove routine.
*/
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) {
ret = pm_runtime_get_sync(dev);
if (ret < 0)
goto out;
}
/* Set the default block size so the driver is sure it's something
* sensible. */
sdio_claim_host(func);
ret = sdio_set_block_size(func, 0);
sdio_release_host(func);
if (ret)
goto disable_runtimepm;
ret = drv->probe(func, id);
if (ret)
goto disable_runtimepm;
return 0;
disable_runtimepm:
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
pm_runtime_put_noidle(dev);
out:
return ret;
}
3、4行不用說了。
第8~10行,之前的match操作也調用的sdio_match_device,不過當時僅僅關心是不是匹配,其他一概不管。這裏需要用到它的sdio_device_id返回值。
第12~22行,sdio host的caps成員記錄其特性(capability),MMC_CAP_POWER_OFF_CARD是電源管理(Power Manager)方面的選項,設置了這個標誌的host在suspend的時候將把card的電關掉。很顯然,在probe的過程中不允許這種情況發生,解決方式就是禁止MMC子系統進入suspend模式。pm_runtime_get_sync()調用後將增加sdio device的引用計數,系統suspend過程中檢測這個引用計數,發現其不爲零就會退出suspend流程。相應的,pm_runtime_put_noidle()降低引用計數,也即它們必須成對使用。
第26~28行,設置block size的時候要做好臨街資源的保護和同步,sdio_claim_host獨佔式地(exclusively)佔用host,用完後調用sdio_release_host進行釋放。控制器作爲host,一個host連接的設備(MMC子系統稱作card)不止一個,所以做好同步是分所應當,比如進程1在使用host操作card a,在進程1工作未完成前進程2不得同時用該host訪問card b;當然,進程1用完後要及時地釋放。
獨佔訪問的實現:
void sdio_claim_host(struct sdio_func *func)
{
mmc_claim_host(func->card->host);
}
static inline void mmc_claim_host(struct mmc_host *host)
{
__mmc_claim_host(host, NULL);
}
/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
* @abort: whether or not the operation should be aborted
*
* Claim a host for a set of operations. If @abort is non null and
* dereference a non-zero value then this will return prematurely with
* that non-zero value without acquiring the lock. Returns zero
* with the lock held otherwise.
*/
int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int stop;
might_sleep();
add_wait_queue(&host->wq, &wait);
spin_lock_irqsave(&host->lock, flags);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
stop = abort ? atomic_read(abort) : 0;
if (stop || !host->claimed || host->claimer == current)
break;
spin_unlock_irqrestore(&host->lock, flags);
schedule();
spin_lock_irqsave(&host->lock, flags);
}
set_current_state(TASK_RUNNING);
if (!stop) {
host->claimed = 1;
host->claimer = current;
host->claim_cnt += 1;
} else
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait);
if (!stop)
mmc_host_enable(host);
return stop;
}
顯然幹事的是__mmc_claim_host,且其第二個參數爲NULL。23行,DECLARE_WAITQUEUE是一個宏,我們把它展開:
#define DECLARE_WAITQUEUE(name, tsk) \
wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)
#define __WAITQUEUE_INITIALIZER(name, tsk) { \
.private = tsk, \
.func = default_wake_function, \
.task_list = { NULL, NULL } }
#define current (get_current())
DECLARE_WAITQUEUE(wait, current);
wait_queue_t wait = {
.private = get_current(),
.func = default_wake_function, \
.task_list = { NULL, NULL }
}
這裏創建一個名爲"wait"的等待隊列並對其成員初始化,設置其private成員爲當前進程。
27行,might_sleep()提醒使用代碼的人,這個函數可能會睡眠。如果在原子型上下文(spinlock, irq-handler, ...)調用,this macro will print a stack trace,當然開啓該功能需要打開相應的config。這裏不多說。
29行,add_wait_queue(&host->wq, &wait),把自己(“wait”)加入到等待隊列中(host->wq)。host->wq在SDIO驅動(2)Host註冊流程提到的mmc_alloc_host()函數中已經初始化。
30行,spin_lock_irqsave(&host->lock, flags),這個spinlock執行三個動作:保存當前中斷標誌(到flag),禁止CPU中斷,獲取lock;自然spin_unlock_irqrestore執行相反的操作。
31~39行之間的 while循環,首先設置進程狀態爲TASK_UNINTERRUPTIBLE不可中斷,進而對信號不做響應,保證當前進程在等待時不會受到干擾。傳進來的第二個參數爲NULL,所以stop等於0。接着進行判斷,如果我們獲取了host的使用權則結束循環,否則恢復中斷、釋放spinlock並進行調度,當前進程進入睡眠狀態等待喚醒。什麼情況下喚醒?另一個進程release
host的時候:
static void mmc_do_release_host(struct mmc_host *host)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
if (--host->claim_cnt) {
/* Release for nested claim */
spin_unlock_irqrestore(&host->lock, flags);
} else {
host->claimed = 0;
host->claimer = NULL;
spin_unlock_irqrestore(&host->lock, flags);
wake_up(&host->wq);
}
}
很清楚了不是?
當前進程被喚醒, set_current_state(TASK_RUNNING)繼續運行。
41~45行,表明host被當前進程佔用了並增加計數。
46行移除等待隊列,50行使能host。
現在我們已經擁有了host的所有權,調用sdio_set_block_size設置通信時一個block的大小,業內叫“block size”:
/**
* sdio_set_block_size - set the block size of an SDIO function
* @func: SDIO function to change
* @blksz: new block size or 0 to use the default.
*
* The default block size is the largest supported by both the function
* and the host, with a maximum of 512 to ensure that arbitrarily sized
* data transfer use the optimal (least) number of commands.
*
* A driver may call this to override the default block size set by the
* core. This can be used to set a block size greater than the maximum
* that reported by the card; it is the driver's responsibility to ensure
* it uses a value that the card supports.
*
* Returns 0 on success, -EINVAL if the host does not support the
* requested block size, or -EIO (etc.) if one of the resultant FBR block
* size register writes failed.
*
*/
int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
{
int ret;
if (blksz > func->card->host->max_blk_size)
return -EINVAL;
if (blksz == 0) {
blksz = min(func->max_blksize, func->card->host->max_blk_size);
blksz = min(blksz, 512u);
}
ret = mmc_io_rw_direct(func->card, 1, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
blksz & 0xff, NULL);
if (ret)
return ret;
ret = mmc_io_rw_direct(func->card, 1, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
(blksz >> 8) & 0xff, NULL);
if (ret)
return ret;
func->cur_blksize = blksz;
return 0;
}
這裏有必要說一下struct sdio_func。一張SDIO卡可以支持很多Functions,一個Function對應軟件上的struct sdio_func結構體,最後反應到驅動模型上就是一個device:
int sdio_add_func(struct sdio_func *func)
{
ret = device_add(&func->dev);
return ret;
}
host通過CMD5獲取卡支持的Functions個數,數據保存在Response R4中:
struct sdio_func的card指針成員表示該設備屬於的card類型。所以24行,如果參數blksz大於host規定最大block size,返回EINVAL錯誤。
27~30行,第二段註釋說的很清楚,就是設置雙方(host和function device)都支持最大block size。
32行,重點來了mmc_io_rw_direct,我們參照它的參數分析:
int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, unsigned addr, u8 in, u8 *out)
card,對應外接的SDIO卡,card的host成員代表該卡連接的host controller,host controller負責和卡通信的具體實現。
write,讀寫標誌,sdio的spec規定:1爲寫數據到sdio卡,0爲讀數據。
fn, function number,一個sdio卡支持不同的功能,這些功能經過編號後就是這裏的function number。function number用3個bit表示,即功能號最大爲7。
這裏fn=0,再次翻開spec:Note that function 0 selects the common I/O area (CIA)。CIA-Common I/O Area:
addr,訪問地址,佔17個bit。
/*
* Function Basic Registers (FBR)
*/
#define SDIO_FBR_BASE(f) ((f) * 0x100) /* base of function f's FBRs */
#define SDIO_FBR_BLKSIZE 0x10 /* block size (2 bytes) */
CIA區域的FBR:
顯而易見,SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE = 0x100+0x10 = 0x110,是FBR的block size寄存器的低8位地址,38行定義高8位地址。
in,寫到sdio卡中的數據,這裏分兩次寫入。
out,如果我們期望得到sdio卡的response,卡的當前狀態信息會被寫入out指向的位置。
經過以上兩次mmc_io_rw_direct()調用的寫入,通信雙方的block size就設置好了,至於數據怎麼打包、怎麼發送、如何獲取迴應數據參見 SDIO驅動(6)命令的構建和發送。設置好後,同步block size到42行的func->cur_blksize成員。
接着32行,調用sdio driver的probe函數,我們以WiFi驅動(1)框架解析提到的RTL8723爲例:
static struct sdio_driver r871xs_drv = {
.probe = rtw_drv_init,
.remove = rtw_dev_remove,
.name = (char*)DRV_NAME,
.id_table = sdio_ids,
};
static int rtw_drv_entry(void)
{
ret = sdio_register_driver(&r871xs_drv);
return ret;
}
那麼這時rtw_drv_init函數會被調用。
最後33~42行,異常處理之前都提到過,沒出意外的話整個probe就結束了,OVER。