所謂同步,即指在一個thread存取資料未結束的時候,其他thread不得存取同一個資料
mutex 與 binary semaphore 的不同點:
mutex 的 acqurie 和 release 需要同一個 thread 執行
也就是 解鈴還需繫鈴人
但binary semaphore 可以由不同的 threads 執行
編譯過程一般可分為六步:
掃描(詞法分析) scanner
語法分析 parser
語意分析 semantic analyer
原始碼最佳化 source code optimizer
目的碼產生 code generator
目的碼最佳化 code optimizer
目的檔的內容:
程式碼區段 code section
=> .text | .code
=> 存放編譯後的machine code
資料區段 data section
=> .data
=> 存放全域變數和區域靜態變數 [已初始化]
定數量未初始化區段 Block Started by Symbol section
=> .bss
=> 存放全域變數和區域靜態變數 [未初始化]
=> 在檔案中不佔據空間
唯讀區段
=> .rodata
=> 存放唯讀變數和字串常數
2012年11月16日 星期五
2012年11月15日 星期四
Linux SDIO driver programming
Open Source 的 standard SDIO stack 大約在 2.6.24 的時候,由 Pierre Ossman 加入 Linux kernel。某種程度上,SDIO stack 是基於 MMC stack 發展出來的,所以蠻多功能會用到 MMC 這個詞。稍微簡介一下檔案放置的位置:
以下節錄自 {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 的陣列,你可以使用下列巨集來產生該陣列成員:
而在填完 struct sdio_driver 之後,你可以使用下列這兩個函數來註冊和註銷 sdio_driver:
註冊完 sdio_driver 之後,系統便可以在裝置插拔的時候呼叫對應的 probe 和 remove 函數。
可以看到這兩個函數的第一個指標參數型別都是 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;
};
這個參數應是 kernel 取得 SDIO device 資訊並填好之後傳過來的,之所以要講到這個參數,是因為 driver code 中會利用到下列巨集設定/取得驅動程式私有的資料結構。
假設驅動程式私有資料結構為 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:
同樣地,做完 IO 之後,需要再 stop 函數之中 disable device:
一些 SDIO 的 Read/Write function 列表如下:
以上的 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
- 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
2012年11月5日 星期一
How to become a Linux Device Driver Programmer
本文只是想就自己的經驗與資料作個整理,讓一些有志開發 LDD (Linux Device Driver) 的新手可以有跡可循,畢竟這些只是自己跌跌撞撞的經驗,說不上參考,但還是希望能夠幫助到某些人。
第四步,作為 kernel 開發者來學習 Linux。
之所以要走這一步的原因是因為開發 LDD 跟 kernel 息息相關,kernel 定義了許多介面要求 LDD 開發者遵守,並也提供了大量的 API 供 LDD 開發者使用。這樣有一個好處是分層可以分得很明確,User space program 跟 driver之間就沒有 dependency 的問題。另外,kernel 是由許多的 subsystem 組成,一個 LDD 不可能不跟這些 subsystem 互動,所以理解 kernel 的組成也是很重要的知識。
這部分我推薦閱讀 Robert Love 寫的「Linux kernel Development」,這本書由一種較宏觀的角度來介紹 kernel,細節沒有觸碰得太多,對於 kernel 初學者來說應該是比較好吸收的一本書。另外就是,除了實作需要之外,LDD 開發者也不太需要整個 kernel 都瞭若指掌,用到的時候再深入理解我覺得會比較符合人性,畢竟 kernel 是一隻蠻恐怖的大怪獸。
第一步,作為使用者來學習 Linux。
這其實是個蠻大的門檻,我覺得在這之中有以下兩點特別重要:
這其實是個蠻大的門檻,我覺得在這之中有以下兩點特別重要:
- Linux kernel 是什麼,它又負責做什麼事情?
- 檔案與目錄管理,包含權限的了解
這部分可以參考「鳥哥的 Linux 私房菜」網站,該站有非常豐富的資源,講解也是簡單易懂!
第二步,學習 C,並嘗試在 Linux 底下開發可以在 Linux 執行的程式。
有了 Linux 的基礎之後,接下來就是自己嘗試開發程式,這時候開發的程式屬於 User space 範疇,我建議可以在此時多多使用各種 IO 相關的 Linux C API。
這部分我推薦閱讀 K&R 的「The C Programming Language」,這本書其實不太適合程式設計新手,所以可能還要另外找一本自己看得順眼的 C 入門書輔助閱讀。其實 LDD 的開發並沒有什麼太難的 C 語法,能夠通盤理解 K&R 的 C 語言聖經本就已經是非常熟稔 C 語言了。
第三步,開發 kernel module。
有了 Linux 的基礎之後,接下來就是自己嘗試開發程式,這時候開發的程式屬於 User space 範疇,我建議可以在此時多多使用各種 IO 相關的 Linux C API。
這部分我推薦閱讀 K&R 的「The C Programming Language」,這本書其實不太適合程式設計新手,所以可能還要另外找一本自己看得順眼的 C 入門書輔助閱讀。其實 LDD 的開發並沒有什麼太難的 C 語法,能夠通盤理解 K&R 的 C 語言聖經本就已經是非常熟稔 C 語言了。
第三步,開發 kernel module。
這一步我自己走的有點亂,也還沒整理好自己的經驗,不過我推薦以下幾本好書,讀者一定能從其中得到收穫。(之後有空再來重寫這段 XD)
Linux Device Driver 3rd Edition, O'REILLY 出版
Linux 裝置驅動程式之開發詳解 第二版, 松崗出版
Linux Device Driver Programming, 博碩出版
Linux Device Driver 3rd Edition, O'REILLY 出版
Linux 裝置驅動程式之開發詳解 第二版, 松崗出版
Linux Device Driver Programming, 博碩出版
第四步,作為 kernel 開發者來學習 Linux。
之所以要走這一步的原因是因為開發 LDD 跟 kernel 息息相關,kernel 定義了許多介面要求 LDD 開發者遵守,並也提供了大量的 API 供 LDD 開發者使用。這樣有一個好處是分層可以分得很明確,User space program 跟 driver之間就沒有 dependency 的問題。另外,kernel 是由許多的 subsystem 組成,一個 LDD 不可能不跟這些 subsystem 互動,所以理解 kernel 的組成也是很重要的知識。
這部分我推薦閱讀 Robert Love 寫的「Linux kernel Development」,這本書由一種較宏觀的角度來介紹 kernel,細節沒有觸碰得太多,對於 kernel 初學者來說應該是比較好吸收的一本書。另外就是,除了實作需要之外,LDD 開發者也不太需要整個 kernel 都瞭若指掌,用到的時候再深入理解我覺得會比較符合人性,畢竟 kernel 是一隻蠻恐怖的大怪獸。
訂閱:
文章 (Atom)