- mmc/host 包含了 host adapter drivers,其中的 sdhci 是本文主要使用的 adapter
- mmc/core 包含了 mmc subsystem code (mmc_core)
- mmc/card 包含了使用 MMC/SDIO bus 的 device drivers,sdio_uart 是一個例子
以下節錄自 {kernel_source_tree}/drivers/mmc/card/sdio_uart.c
------------------------------------------------------------------------------------
static const struct sdio_device_id sdio_uart_ids[] = {
{ SDIO_DEVICE_CLASS(SDIO_CLASS_UART) },
{ SDIO_DEVICE_CLASS(SDIO_CLASS_GPS) },
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(sdio, sdio_uart_ids);
static struct sdio_driver sdio_uart_driver = {
.probe = sdio_uart_probe,
.remove = sdio_uart_remove,
.name = "sdio_uart", /* 驅動程式名稱 */
.id_table = sdio_uart_ids,
};
------------------------------------------------------------------------------------
有寫過 Linux USB device driver 的人,應該會覺得 SDIO 註冊 device driver 的方式非常類似於 USB,寫起 SDIO 版本的驅動應該就比較不陌生。首先是要先填寫 struct sdio_driver 的結構體,填寫 struct sdio_driver 所要準備的有兩個 callback function 成員,一個是 probe,另一個是 remove,即探測和移除函數,它們分別在裝置插入和拔出的時候被呼叫,用於初始化和釋放軟硬體資源。而 struct sdio_driver 的 id_table 成員描述了這個 SDIO 驅動程式所支援的裝置清單,它指向一個 sdio_device_id 的陣列,你可以使用下列巨集來產生該陣列成員:
- SDIO_DEVICE( vendor, device )
- SDIO_DEVICE_CLASS( device_class )
而在填完 struct sdio_driver 之後,你可以使用下列這兩個函數來註冊和註銷 sdio_driver:
- int sdio_register_driver(struct sdio_driver *);
- void sdio_unregister_driver(struct sdio_driver *);
註冊完 sdio_driver 之後,系統便可以在裝置插拔的時候呼叫對應的 probe 和 remove 函數。
- int (*probe)(struct sdio_func *, const struct sdio_device_id *);
- void (*remove)(struct sdio_func *);
可以看到這兩個函數的第一個指標參數型別都是 struct sdio_func,定義如下:
/*
* SDIO function devices
*/
struct sdio_func {
struct mmc_card *card; /* the card this device belongs to */
struct device dev; /* the device */
sdio_irq_handler_t *irq_handler; /* IRQ callback */
unsigned int num; /* function number */
unsigned char class; /* standard interface class */
unsigned short vendor; /* vendor id */
unsigned short device; /* device id */
unsigned max_blksize; /* maximum block size */
unsigned cur_blksize; /* current block size */
unsigned enable_timeout; /* max enable timeout in msec */
unsigned int state; /* function state */
#define SDIO_STATE_PRESENT (1<<0 color="#0000ff" font="font" nbsp="nbsp">0>
/* present in sysfs */
u8 tmpbuf[4]; /* DMA:able scratch buffer */
unsigned num_info; /* number of info strings */
const char **info; /* info strings */
struct sdio_func_tuple *tuples;
};
- sdio_get_drvdata(struct sdio_func*)
- sdio_set_drvdata(struct sdio_func*, void *data)
假設驅動程式私有資料結構為 struct foo_priv 且其內嵌 struct sdio_func,舉例如下:
static int foo_probe( struct sdio_func *func, const struct sdio_device_id *id )
{
struct foo_priv *priv = kmalloc( sizeof(struct foo_priv), GFP_KERNEL );
priv->func = func;
sdio_set_drvdata(func, priv);
}
static void foo_remove( struct sdio_func *func )
{
struct foo_priv *priv = sdio_get_drvdata(func);
priv->func = NULL;
}
接下來,在與 SDIO device 做 IO 之前,需要在 start 函數中 enable device:
- sdio_claim_host(priv->func); //取得 MMC host controller 的物理使用權
- ret = sdio_enable_func(priv->func); //Enable SDIO function
- ret = sdio_claim_irq(priv->func, foo_irq); //註冊 IRQ handler
- sdio_release_host(priv->func); //歸還 MMC host controller 的物理使用權
同樣地,做完 IO 之後,需要再 stop 函數之中 disable device:
- sdio_claim_host(priv->func); //取得 MMC host controller 的物理使用權
- sdio_release_irq(priv->func); //Disable SDIO function
- sdio_disable_func(priv->func); //註銷 IRQ handler
- sdio_release_host(priv->func); //歸還 MMC host controller 的物理使用權
一些 SDIO 的 Read/Write function 列表如下:
- 1: sdio_readb
- 2: sdio_readw
- 4: sdio_readl
- 1: sdio_writeb
- 2: sdio_writew
- 4: sdio_writel
以上的 SDIO R/W functions 都是基於 mmc_io_rw_direct() 發展出來的。
最後,一個完整實作的例子可以參考
{kernel_source_tree}/drivers/mmc/card/sdio_uart.c
作者 Nicolas Pitre 是利用 SDIO interface 實作一個 UART driver,所以對上層 USER space 的介面是 TTY driver,可是底層 IO 的部分利用到 SDIO 提供的 Read/Write API 做讀寫。
[Reference]
Linux kernel source
http://www.varsanofiev.com/inside/WritingLinuxSDIODrivers.htm
http://blog.csdn.net/gangyanliang/article/details/6873578
沒有留言:
張貼留言