顯示具有 linux driver 標籤的文章。 顯示所有文章
顯示具有 linux driver 標籤的文章。 顯示所有文章

2012年7月14日 星期六

udev的流程

udev主要功能

  • 允許動態建立刪除裝置
  • 自行配置裝置編號
  • 根據規則建立名稱,而非固定名稱
  • 建立kobject物件


udev其實利用了kobject在/sys下面建立的動態的系統資訊,udev主要成分,namedev、libsys、udev

udev配合hotplug機制,每當有裝置加入的時候,建立sysfs(kobject)下的相對應節點,跟著呼叫namedev在/dev下建立相對應的裝置
如果理解bus、device與driver三者的關係,可以理解到udev功能是可以達成的,但是接著升起的疑問是,流程是如何呢?

bus會利用match這個操作方式尋找合適的device以及driver,但是因為需要kobject來在sysfs下建立相對應的結構,是否意味著要改寫driver?從參考資料的解說,應該是不必。udev會監控linux載入的module,自動生成kobject

所以一開始就註冊了bus,udev會在module載入的時候紀錄準備生成kobject,當裝置插入主機的時候,跟著建立device,使用bus尋找到相關driver,最後udev會生成kobject,也就是在/sys下建立相對應的目錄

bus(開機時候)=>driver/udev(載入module時候)=>device(裝置插入主機接上某個bus)=>/dev/xxx與/sys/bus/xxx建立(udev透過bus.match建立)

以上就是我的理解,不知道有沒有錯誤

參考資料:
http://daydreamer.idv.tw/rewrite.php/read-49.html

2012年7月4日 星期三

character driver的岔路上

不能免俗,絕大多數入門的linux driver,除了第一個hello world module之外,跟的就是character driver,因為他最貼近一般人使用fopen, fread, fwrite, fclose的直覺
推薦服用宋寶華的範例,但是在2ed chapter 6使用的測試指令為
echo "hello world"> /dev/globalmem
cat /dev/globalmem
以上可以正常運作,可是跟著我就開始試著其他組合
echo "hello world"> /dev/globalmem
echo "again"> /dev/globalmem
cat /dev/globalmem
開始似乎不大正常的運作,即使使用
echo "hello world"> /dev/globalmem
echo "again">> /dev/globalmem
cat /dev/globalmem
也於事無補,我開始懷疑ppos這個表示目前檔案位置的參數有沒以正確運作,在經過一番追蹤,似乎發現不如預期。我恍然大悟,原來要從process、file、inode角度去切入,就明白這一切了。


過程中我也寫了個簡單的程式,使用fopen, fread, fwrite, fclose去操作,因為一點點小小的失誤反而讓我看到一些覺得奇怪的地方,首先fopen, fread, fwrite, fclose的應該是對應到了driver內的operations,但是我在fopen的時候,最後讀寫模式寫錯了"w+",只寫了"w",結果編譯過程還是通過了,但是fread沒有作用,照樣有輸出,真是好神奇阿@@a有興趣的人可以用strace試驗看看

cross compile strace

指令如下
CC=arm-linux-gcc LD=arm-linux-ld RANLIB=arm-linux-ranlib ./configure --host=arm-linux --target=arm-linux --prefix=./install_dir
完成之後直接放到Target上就可以了
相關資料
https://www.ibm.com/developerworks/cn/linux/l-tsl/

ioremap的理由

參考資料內說得非常好,有許多理由
1. 一般io access, 有所謂的IO bus或使用memory mapped IO, 以I386而言,   它有in/out系列的instruction來做IO bus access, 而ARM則沒有. 
2. 對於IO bus access, 一般driver只要有access right就可, 而由於一般而   言C/C++ 無法generate in/out系列的instruction, 所以要以inline   assembly來達成這個目的, 而一般的inb(), inw(), outb(), outw()...等   等的functions或macros會可以做到這件事. 
3. 對於memory mapped IO, 由於是直接access memory, 必須跟OS的memory   system結合, 所以就會有一些access right, non-cacheable, virtual   address等的問題. 
4. 對PCI而言, 還有所謂的PCI bus address, 而一般PCI bus driver在啟動   後會enumerate上面的card, 了解它們的memory regions的size, 並assign   PCI bus address給每一memory region, 至於如何由system bus address   對到PCI bus address要看SOC的設計. 在access時, 是virtual address->   physical address->PCI bus address. 
5. OS並不會知道那一塊被拿來當memory mapped IO, 所以, 往往是只有開放   RAM的部份的space, 其實一般startup code其MMU的部份還會打開SOC的   register space或一些local bus banks的space, 以相同的virtual address   來使用對應的physical address, 但是, startup code一般我們希望不要   放太多變動性太大的東西, 這樣也會把太多枝枝葉葉的codes放入主要程式   中, 另外, 與PCI相關的考量放入startup code, 也會變得很繁瑣. 所以   ioremap很重要的是就是讓driver可以為它要使用的空間找到一塊virtual   address的space. 
6. 有一些扁平式的設計, 直接一開始全部對應好, 將4G virtual address對應   到等值的physical address, 所以你不用ioremap也可使用正常, 不過, 使   用ioremap也沒甚麼不好吧, 反而是養成良好的習慣. 而通常, 有virtual   memory的MMU, 一般都是只開有限的區域, 即使它們只是直接對應等值的   address, 因為這樣可以加強系統的debug功能, report非法的memory access. 
7. 一般ARM有分TLB或protection unit的, protection unit只做保護和cache/   write buffer的控制, TLB則可以實現virtual memory.

因為一般在kernel模式下操作的是virtual address,透過MMU的page table來處理virtual address與physical address,但是實體io port或者memory,要是直接存取會產生兩個問題,一個是受限於特殊硬體架構,另外一個是不應直接處理physical address。於是乎ioremap就是透過修改page table,讓physical address對應到virtual address(等於在virtual address規劃出一區塊,使OS知道使用該區塊等同在使用某區塊的physical address),這樣一來就某種程度隔開特殊的硬體架構,也避免直接使用physical address。

跟著由於硬體的特性,雖然ioreamp讓程式設計師使用實體記憶體如同使用一般memory一樣,但是因為有時硬體可能只是個io port,訊號隨著時間一直變動(有點類似volatile變數一樣),所以在讀取跟寫入的時候最好透過特殊函數如writel或者readl之類,不要直接用assign operator(等號)

http://www.programmer-club.com/showSameTitleN/embedded/1820.html
http://blog.csdn.net/do2jiang/article/details/5450839

2012年7月1日 星期日

inode、file以及container_of

回顧LDD3的第一個character device範例,其實感覺裡面一次給出了太多東西,會令人暈頭轉向,其實在宋寶華的書中給出一個比較簡單的範例


  • LDD3一次配置多個device,對多個device視為同一類,使用同一個module
  • 宋寶華一開始只有一個device,只給定一個module


兩者之間的差異決定的對程式展現的方式,其中以container_of這個macro最為經典,建議先看完宋寶華的範例,再瀏覽LDD3的範例,可以知道這個macro的關鍵性作用


LDD3展示了,一個module可以同時為同一類的裝置提供驅動程式,讓device(裝置)與operations(操作),可以結合,同時可以分開,一個module提供多個裝置,相同的操作,其中隱含了物件或者說ADT(abstract data type)的概念,這個在driver開方上很重要,udev更是把這種概念發揚光大

inode跟file兩個重要的結構提供了不同的作用,inode大多存在於kernel space,file則是user/kernel space都有(使用者一般操作file),同時也是支撐VFS (virtual file system)的兩個重要結構


  • file結構有兩個重要成員,一個是file_operations,也就是操作file的functions,另外一個是private_data指標,通常module會將對應的device資料的記憶體位置交給private_data
  • inode則擁有i_cdev或者i_bdev,分別表示character device或者block device這兩個指標,雖然一般開發者會重新包裝自己的結構,將struct cdev包裝起來,以支援定義的驅動程式,如LDD3範例


struct scull_dev {
struct scull_qset *data;  /* Pointer to first quantum set */
int quantum;              /* the current quantum size */
int qset;                 /* the current array size */
unsigned long size;       /* amount of data stored here */
unsigned int access_key;  /* used by sculluid and scullpriv */
struct semaphore sem;     /* mutual exclusion semaphore     */
struct cdev cdev;  /* Char device structure */
};

struct scull_dev *scull_devices; /* allocated in scull_init_module */
最後一個scull_devices表示一堆同類的devices

有了以上的瞭解之後來看open、read函數
static int open(struct inode *inode,struct file *filp){
...
}


static ssize_t read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){
...
}

在LDD3的範例在init函數配置好了所有的scull dev(因為不只一個device),但是在open函數的時候要如何找到對應的scull_devices中的哪一個?因為我們想把他assign給file.private_data方便以後給read函數使用
一般而言,由於inode包含了cdev這個變數,我們知道cdev來自於scull_dev裡面,所以會想藉此找出scull_dev,裡面包含了如何計算cdev應該在scull_dev內的位移,配合位移,藉由調整記憶體的address,我們就可以找到scull_dev了,而此時container_of就可以幫我們忙

struct scull_dev *dev; /* device information */


dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
constainer_of這個macro會自動完成計算位移以及轉型的動作,將目前打開的device所指向的scull_dev變數找出來。也就是只有scull_device.cdev結構成員,卻可以找到scull_dev的結構變數。做了那麼多工作,就是為了把scull_device配置給filp->private_data,後面read函數就可以應用她了

如果今天只有一個device,那麼其實也不用那麼麻煩,就如宋寶華的範例,直接將struct scull_dev onlyone設定為global變數,然後直接在open函數的時候assign給filp->private_data即可


由上面可以了解到,LDD3在一個範例內展示了

  • VFS這樣的抽象操作需要file以及inode配合
  • scull這個module可以同時支援多個一樣的裝置
  • 如何註冊character device,以及要注意的事項
  • 如何實作file operations
  • 隱含在Linux Device Driver中分層以及分工的架構
  • ...

不細看還真的很難體驗這個module竟然有如此多的東西,也感嘆這章節竟然寫得如是簡單/簡潔,真的不知道LDD3的作者是故意的還是假設讀者的Linux背景很夠

如果有興趣了解container_of到底如何運作,可以參考參考資料連結

參考資料:
http://space.itpub.net/14805538/viewspace-445624
http://tw.myblog.yahoo.com/hughes-blog/article?mid=88&prev=-1&next=87

2012年6月30日 星期六

Hello World Module

不能免俗的一個hello world等級的module,基本上就是學習編譯技巧跟觀察,一點作用都沒有
檔案為hello.c位於某個版本的kernel source下的drivers/hello目錄內,比如說/home/account/friendarm/kernel/linux-2.6.32.2/hello,底下有兩個檔案hello.c跟Makefile
Makefile只有兩行,一行指定compiler,另外一行就是module的相依行囉
編譯指令為
make -C /home/account/friendarm/kernel/linux-2.6.32.2 M='drivers/hello' modules
-C後面的參數為kernel source tree位置,後面則是要編譯的module的位置

我跟著是放到micro2440上面執行(如果在桌上型,Makefile內的CC可以省略,直接用系統內建就可以了),因為micro2440上面預設把所有訊息打開了,所以可以在console底下看到訊息輸出,不然一般應該在/var/log/messages檔案內

2012年6月28日 星期四

linux input subsystem

linux input的subsystem已經發展許久,為了簡化driver,系統發展出了input core的部分,不過也是因為這樣,使用者在使用方便之餘,已經搞不清楚到底事件的觸發是如何從source to destination了,還好linux是開放原始碼,所以trace source code還是可以瞧出端倪的

很多書本不知道是為了哪種理由,其實這部分大多很含糊的略過,只簡單提到使用input_register_device()去註冊由input_allocate_device()配置出來的device,然後跟著使用input_event()跟input_sync()完成工作,最多再給出個虛擬的滑鼠的範例,結束

看完之後我還是搞不懂,事件如何導向消化的地方,我可否自行寫一個輸入系統,再自行寫一個handler來處理呢?直覺上是可行的,但是如果不了解如何運作,恐怕也是很難做到

透過drivers/input/input.c的source code解析可以得到答案,不過網路上已經有人剖析得很清楚,我就直接借用連結過來吧
http://blog.163.com/wxiongn@126/blog/static/1178820382010724104617786/
http://blog.163.com/wxiongn@126/blog/static/117882038201072410502312/

2012年6月16日 星期六

個人認為的linux驅動程式學習步驟

linux驅動程式開發的門檻比一般程式設計的門檻高,原因在於對於硬體及韌體的了解需要比一般的更多,相較之下,如web service的開發則傾向設計的門檻比較高,比方說MVC的應用,以及類似DB的設計等等,幾乎是不同的走向

linux驅動程式開發最好對硬體有些了解(本人對硬體就很不了解,花了不少時間填補),再者對於一些作業系統的基本元件,如process、semaphore...等等要有概念,接著了解system programming(指那些system call),跟著可以開始作驅動程式開發

  • 如果是嵌入式系統,可以overview各種cpu、chip、memory、hardware interfaces開始;跟著必須補齊如boot loader、kernel、root system等等概念,oreilly的"建構嵌入式linux系統"是本這方面的好書,相對的PC上的linux就比較沒有這方面的疑問,很多都已經非常的標準化了,變化行不如嵌入式系統大
  • 基本的裝置分類方式character、block、queue,學習linux從高階的角度看待各種設備。
  • 驅動程式IO的元素,如blocking/non-blocking、synchronous/asynchronous、critical section的維護、device/device_memory IO、記憶體的操作(kmalloc、__get_free_page...)、interrupt。因為驅動程式主要就是為了輸入、輸出訊號,這些元素都是必備的
  • 接著了解bus/device/driver的架構、分層(layer)、分離、子系統(input、usb...)的應用。這部份有些書本略過了,真的很難消化,網路上資源不少,但是又很少願意以圖形說明,如果只有自己爬code會很累
  • 在貼近各種bus界面,以及硬體的控制,如PCI、USB、I2C...
  • 其他還有debug的技巧

整體linux的學習過程應該要花上好些時間,再者如果每個部份又不是很熟悉又很容易因為一同栽入,見樹不見林,我想提供一個簡單的方向讓有心的人可以稍微有點概念。
我認為最難的兩個部份,一個是linux driver的架構、各種子系統的設計,其實因為發展了許久,所以開始結構化,但是結構化就是簡化程式碼,但是提昇程式碼之間的相關性,再不了解相關性(如子系統的原理),看code就倍感吃力;其次是各種硬體跟界面的經驗,跟界面也都有他的設計的經驗跟原則,當使用者或者一般程式設計師都有OS幫忙處理,所以現在要寫這一層,就比較吃力一點。最後,當然最好的是身邊有個熟悉的人,可以讓你一直問:P但是這真的是看緣份了

最後應該就是硬體的資訊,這方面不屬於韌體工程是的範疇,但是又有點難分割,在業界有vendor廠商提供主要的幫忙,但是了解愈多愈能幫助自己完成工作

最後我想說,相較於四、五年前現在資料真的多許多,主要是拜android風行之賜,相關資料一下子有了爆炸行的成長

2012年6月15日 星期五

linux驅動程式-chapter 7

這個章節看似比較鬆散一點,不過如果從了解中斷的角度應該會比較容易入手一點
先想想中斷,中斷分類可以分成幾種

  • 從來源,分為內部中斷(cpu引起,如除0)跟外部中斷(硬體週邊引起)
  • 從是否可遮罩,分成可遮罩與不可遮罩
  • 從中斷處理函數,分成向量中斷跟非向量中斷

不論如何,基本的處理精神都是希望趕緊將中斷處理完畢跟著把控制權交還給本來的程式,很可惜有的中斷要長時間處理,比方鏡頭要拷貝整個影像,這需要長時間
所以很多OS設計就將中斷分成兩個部份,一個部份是處理引發的事情,讀入一些需要的資料(如暫存器),記憶體拷貝的部份在另外找時間處理,以如此折衷的方式進行。前面稱為中斷的top half,後續的處理過程稱為bottom half。

跟著我們知道有個獨特的中斷為計時中斷,很多軟體的timer也是基於此設計,書中先介紹不使用OS支援的方式實作timer,跟著引入有OS支援的方式

一開始介紹了如何使用jiffies來計算時間,每個jiffiers是一次震盪的時間(不管是cpu或者是主機板上的石英振盪器),一開始書本給的範例是使用busy waiting的方式來計時,也就是讓程式一直消耗cpu,且反覆觀察jiffies的變化,透過time_after、time_before等函數來比較jiffies變數之間的大小,進而達成timer的作法
很明顯busy waiting的方式當然是不好的,與其等待不如把cpu讓給有需要的人。另外就是OS為了服務更多的timer(想像很多process需要定時執行某些事情,這些process需要很多timers),於是OS維護了一個timers的list,每隔一段時間就檢驗這些list,並且將那些時間到了的process放入排程(schedule)之中
而這樣的作法就是將那些process視為half bottom,而kernel中檢驗list中的timer變成了half top,中斷發生的時候就快速篩選timer並且進入排程
一個簡化的版本是呼叫schedule_timeout()該函數中是使用timer_list來維護

接著談到中斷的bottom half的實作方式有tasklet與work queue的方式

2012年6月14日 星期四

Linux之AIO

雖然這已經是很久之前就存在的技術,但是感覺在一般的程式設計書籍並不常見(應該說比例上),但是最近AIO卻是延伸到web server上變得火紅,從node.js可見一斑
主要是因為閱讀蒐集linux driver的資料的時候看到的,因為感覺LDD這本書有點看不懂,可能礙於實際開發經驗,以及程式設計背景知識,即使知道了如何寫出driver,但是感覺並無法實際充分利用。所以我也另外買了一本大陸作者的書籍"Linux 裝置驅動程式之開發詳解(第2版)",前面講解還算通暢,可是到了AIO左右已經開始有點不知所云,轉而向網路搜尋,找到了M. Tim Jones的著作,他並非亞洲人
http://www.ibm.com/developerworks/cn/linux/l-async/
發現原來該書的作者應該是瞟竊他人的著作!!??雖然後來唸到後面有看到一個URL指向該文章,也不知道這個網路文章(已經翻譯成中文)是否為書本作者翻譯的,但是完全不加以註明,讓人覺得書本文章是他自己寫的,書中一段文字幾乎跟網頁文字相同!!再加上範例也很像。
我發現難以理解主要是因為書本作者根本把一些重要的網頁圖片拿掉,如下面的圖片,明白點出blocking/nonblocking跟asynchronous/synchronous之間衍生的關係


建議對書中看得不大懂的讀者,轉向直接閱讀該網頁的內容,其中還有四個圖案講解kernel跟這四種IO之間的關係,簡明易懂,個人看了感覺大有收穫

2012年5月16日 星期三

Linux驅動程式--chapter 13

這一章節主要介紹USB,這也是linux中相當複雜的一部份,書中感覺也是概略性介紹,我在網路上爬了一些資源,先記錄下來,再回頭來寫摘要

相關資源:
http://www.ibm.com/developerworks/cn/linux/l-usb/index1.html
http://www.ibm.com/developerworks/cn/linux/l-usb/index2.html
http://blog.csdn.net/fudan_abc/article/details/6820580
http://kezeodsnx.pixnet.net/blog/post/27794098-%5B%E8%BD%89%E8%B2%BC%5D-linux-%E4%BD%BF%E7%94%A8-usb-%E8%A3%9D%E7%BD%AE%E7%AD%86%E8%A8%98-
http://linux.chinaitlab.com/driver/877123.html
http://lwn.net/Articles/143397/

Linux驅動程式

已經好久沒更新了:P,因為最近都忙著補充以前"流失"的硬體相關資訊,書本囫圇吞棗的已經看到chapter 13去了,但是都沒整理。發現這linux driver真的是個大課題,然而這本書很可惜無法面面俱到(如果要寫的詳細,我想上千頁應該是基本的吧),書本優點是已經將大多需要用到的原則以及重要的原件都提到,可是底下是不足的地方

  • 後面章節的範例愈來愈缺乏實作性質。也就是大多屬於simple code的範疇,並無法很順利地跟實際開發相連
  • 與硬體連接的部分沒有更多的敘述。我想這點應該是取捨的問題,不大算是不足,不過如果需要硬體底層的資訊,可能還要在網路或者其他書籍中尋找
  • 與kernel相關的部分著墨不過多。driver往往與kernel相關,書中並沒有比較詳細的闡明兩者之間的關係,或許是作者想要在抽象層面做介紹,如果與kernel連結,又不綁定特定平台,恐怕書本真的太厚
  • 後面章節流於形式介紹。可能作者對於驅動程式開發太熟悉,內容看起來都很類似steps 1, 2, ...,並每有很生動的場景(scenario),對於理解幫助有限

我想把這本書看完之後,應該會在去找上一兩本linux driver的書籍來看吧,另外開始研究linux核心到底做了甚麼事,當然同時不可避免地必須了解硬體的一些相關資訊

2012年3月28日 星期三

Linux驅動程式--chapter 5

這章的重點是concurrency的問題,介紹的工具是semaphore、mutexes、spinlock、atomic operations、seqlock、Read-Copy-Update(RCU)
基本上快都是作業系統(OS)介紹過的東西,如果上過這個課程,對這些東西應該不陌生。雖然很想這樣結束,但是我要說,書中介紹的原則往往比教科書來的多,也來的更貼近實際狀況;另外也讓我體驗到driver有時候設計真的充滿了trade off

一般而言,我們希望使用semaphore因為會比spinlock容易(因為高階),但是spinlock會比較有效率,但是spinlock又可能引起沒效率,這種弔詭的說法有舉例

一般而言,spinlock使用的時候會取消cpu preemption的功能,希望目前的task趕緊完成工作,儘快釋放lock,很不幸很多function call會引發目前的工作釋放cpu,比方說kmalloc,而且沒有完整的列表。其次中斷也會引發目前task帶著lock且釋放出cpu,這時候可能會有很不幸的事情發生,那就是如果該中斷要求同一個spinlock,這就好玩了,他會因為拿不到,重新觸發,然後就一直浪費cpu的時間在等待task釋放cpu,但是可能又因為cpu preemption功能被關閉,本來的task進不到cpu產生了dead lock,如果不會dead lock也有可能cpu空轉上好一段時間。這也是為何說spin lock可能引起效率低落的原因。

作者給spin lock幾個建議:

  • 使用spin lock不要有cpu preemption,這點kernel會處理,不至於太擔心
  • 使用spin lock不要讓task sleep,要非常小心,因為如上所說function call & interrupt
  • 使用spin lock要儘快釋放,免得schedule的功能"失去作用",因為spin lock取走了大部分cpu時間

對於種種同步的建議是:

  • 儘量事前規劃好,省得事後debug很困難
  • 對於鎖定的資源依序取得,可以避免一些dead lock的可能性
  • 不要呼叫交互取得lock或者資源的function,這樣也很容易dead lock


atomic operation則適用於int以及bit運算,因為有時候我們希望有些簡單但是重要的count功能或者加減的功能是使用atomic operations,但是有點要認知到,就是運算的串聯次序本身並沒有被保證,也就是兩個以上task執行了各十個atomic operations,你並不能知道他們之間的次序是如何排列的。
至於詳細的API使用方式,可以參考書本

2012年3月27日 星期二

Linux驅動程式--chapter 4

這一章已經超過了單純driver,還包含到了kerenel debug的技巧,首先介紹一些在kernel hacking裡面的選項,為了debug有時候是必須打開的,接著介紹不同的技巧以及他們應用的時機
printk() : 最簡單最直接的一種,大致上分成八個level只有當設定的訊息的level小於console_loglevel的時候,訊息才會直接被送到console,作者還介紹了一個小工具程式,讓我們可以設定輸出的console。接著printk的問題就是如果在code裡面埋了一堆debugger information,要如何在編譯階段可以清理掉,作者定義了幾個macro來處理這件事情

/proc : 從2.6開始procfs被引入作為顯示系統狀態的vfs,如果老是使用printk在動作很多的時候,訊息量可以淹沒整個console畫面,反過來如果可以在適當時機將需要的資料拿出來看,這樣除錯就可以輕鬆許多,要在/proc底下註冊建立/proc檔案需要用到
struct proc_dir_entry *create_proc_read_entery(const char *name, mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc, void *data)
這一個function,可以註冊需要的路徑跟虛擬裝置,同時要提供可以讀取資料的函數(跟自己寫的module在同一檔案/空間之中),將目前module的狀態讓使用者可以用類似cat /proc/scullmem的方式取出。這種方式有個限制就是基本上是以page size,在運作,操作複雜結構的資訊並不是很容易,另外就是/proc已經開始有濫用的趨勢,/proc已經不只是讀取,還可以寫入,有點跟/dev一樣的問題。linux已經開始引入sysfs來處理這樣的問題,他要求更有架構性的資料

ioctl() : 最靈活的方式,但是程式設計師必須自行處理,在此章節作者也還不打算詳細介紹,他另外有個好處就是code獨立,不是類似printk會"汙染"到原本的code,所以忘記把它拿掉只是佔據記憶體空間:P
strace : 用來追蹤一些system call的次序,但是可讀性低於printk,有許多參數,如-o表示將輸出轉移到檔案內
strace -o /tmp/x1 ls -R > /dev/scull0

Ooops information : 當系統fault的時候(不是panic/死當),會產生類似coredump檔案的輸出,包含了CPU register等等information(P.S>我不大清楚它會產生到哪裡去@@a)

magic SysRq key : 一些特殊的組合鍵,讓使用者可以做一些事情,請參考書內內容

debugger tools : gdb、kdb、kgdb、Linux Trace Toolkit (LTT)、Dynamic Probes

裡面有提到一個有趣的User-Mode Linux,這是一個虛擬機器的機構,但是她不能讓使用者直接存取硬體

最後,其實作者的sample code的scull包含了chapter 3跟4,所以如果光看了chapter 3就去看code會感到怎麼還有很多多出來的東西

參考資料:
http://zh.wikipedia.org/wiki/Sysfs
http://blog.linux.org.tw/~asho/archives/001816.html
http://184.82.2.112/wordpress/?p=505

2012年3月24日 星期六

Linux驅動程式--chapter 3

對於新手還真的有點辛苦的一章,一邊念著一邊看著原始碼,這一章節作者寫了個scull的character device的module,先利用了一些script(mknod)將這些虛擬裝置建構出來

首先替module本身與裝置連結,所以要註冊裝置的編號,在linux 2.6中使用major number跟minor number組成一個獨一無二的編號
MKDEV(scull_major, scull_minor)
跟著使用register_chrdev_region註冊數個裝置(依序,如果之前minor number是2, count是3,就會註冊2~4這幾個裝置),或者使用alloc_chrdev_region動態配置

因為作者使用記憶體取代實際可能為磁碟或者其他硬體裝得部份,所以他有些結構需要初始化,scull_devices這個變數對應每個虛擬裝置的相關資料

接著填充一些必要得功能,其中最重要的是struct file_operations這個結構,他包含了許多character device應該具備有的功能,如果沒有的話則保持NULL,如果使用者使用了不再表格內的function call,就會回傳錯誤。底下是一個範例

struct file_operations scull_fops = {
.owner =    THIS_MODULE,
.llseek =   scull_llseek,
.read =     scull_read,
.write =    scull_write,
.ioctl =    scull_ioctl,
.open =     scull_open,
.release =  scull_release,
};

該結構內並不完全都是function pointer,其中.owner就是表示這個module本身,THIS_MODULE是一個macro
另外可以看到就是read/write、llseek跟open/release這幾個常見的操作,但是為何不是close而是release,這是因為linux一般會對裝置紀錄一個count,表示有多少人或者process這在使用這個資源,只有當count為0的時候才會真真的close

接著必須對每個device作file_operations跟device的對應,使用cdev_init輸入兩個參數,分別是該裝置所需用到的資料結構(struct scull_dev)以及file_operations這個結構,作者假設有四個裝置,就分配要配置四份,但是file_operations這些操作是共用的

這裡有點好玩的是,並沒有強制規定同樣的file device必須有同樣的,甚至因為傳入的是指標,表示可以在半路抽換介面@@a,真是一個好玩的狀況!!

仔細觀察file_operations就會發見到,很多地方需要使用到struct file以及struct inode這兩個參數(由kernel傳入),inode裡面有個成員i_cdev,這裡面包含之前用cdev_init配給某個device的資料結構,需要透過struct scull_dev *dev = container_of(inode->i_cdev, struct scull_dev, cdev);這個巨集取出,這樣就可以知道process正在存取哪個device,並且使用相對應的資料空間

另外討論到記憶體的管理,如作者之前提過得,在kernel space並不能使用一般的libc函數,也就是malloc/free之類的記憶體配置不能使用,這裡必須使用kmalloc/kfree。另外值得注意的一點是如何在user space以及kernel space之間傳遞資料,這裡使用
unsigned long copy_to_user(void __user *to, const void *from, unsigned long count)
unsigned long copy_from_user(void *to, const void __user*from, unsigned long count)
在這兩個呼叫,kernel還必須要檢查適當地存取權限,還有處理使用者指向的記憶體可能是page fault的問題

所以整體來說,簡化的架構就是

  • 註冊裝置
  • 初始化相對應裝置的資料
  • 完成file_operations表格
  • 完成相對應操作的函數 
  • 最後記得要清理資料

2012年3月23日 星期五

linux kernel的相關資料

這裡只是剛好爬文到網路上一個不錯的文庫,紀錄一下
驅動程序編寫基本流程              
Linux內核的ioctl函數學習
linux內核中struct file_operations結構體介紹
幾種設備的相關開發文件:
Linux音頻編程指南
嵌入式系統中LCD驅動的實現原理

因為linux驅動程式一書在這個部份寫得比較簡略的,直接翻code在對照文中所寫,就會有豁然開朗的感覺,畢竟第一次接觸linux較完整module(相對於第二章來說,第三章的範例也完整許多),其中也稍微解釋到我以前的疑惑,kernel space與user space的資料交換


有找到一些資料,但是比較無法分類,順便歸類在這裡好了
ARM MMU工作原理剖析
WIFI環境建構

2012年3月22日 星期四

LCD之framebuffer(4)

終於在網路上看到為何framebuffer會比實際上來的大,以前有人會解釋說是為了作double-buffer,但是有時候size又不是單純的一倍大小,這樣很難自圓其說,後來發現有另外一個原因,那就是為了smooth moving,如果有打過一些以前DOS小遊戲的人(有不小心透露出自己年紀不小嗎XD),遊戲主角移動單位是一格一格的,但是移動的時候卻是平滑移動的動畫,不是跳格的方式,這時候多出來的framebuffer空間就可以作為平滑移動,只要改變指標的位置,就不用會產生跳格的感覺

另外經由Linux驅動程式一書的解釋,framebuffer屬於user space的driver,所以效能會比kernel space來的差,所以表示,如果說要使用圖形顯示,但是又想繞過xwin,或許framebuffer是個不錯的選擇,但是想要在比desktop還要差的硬體下壓榨出更多的效能,可能就得自己對driver開刀了,這恐怕是個大工程,當然很多人會想要使用DSP硬體支援,這也是時下主流平板的選擇

2012年3月21日 星期三

Linux驅動程式--chapter 2

這一章節簡單的給了個module的入門,一個只是用來insmod跟rmmod的moduule

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int __init hello_init(void){
  printk(KERN_ALERT "Hello, world\n");
  return 0;
}
static void __exit hello exit(void){
  printk("KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
與一般user space不同,由於module運行於kernel space,所以並不連結一般的lib,所以類似printf就不能使用,只能使用printk,KERN_ALERT是一個macro,後面沒有逗點,module_init點出insmod module就運行的function,相對的module_exit則是rmmod的時候

設計module必須考量到

  • 是kernel space or user space(有些驅動程式是可以為user space,但是有好有壞)
  • module變數的race condition
  • module必須是reentrant
  • module的lifetime幾乎等同kernel,從insmod到rmmod(往往就是開機到關機),中間變數一直存在,並不隨著被服務的process結束而結束
  • module的記憶體有限,堆疊不可能配置太大的空間
  • module往往跟目前服務的process有很大的關係,可以使用struct task_struct中的current指向目前的process
  • module的相依性處理不完全在於程式,在於管理者必須知道使用modprobe來追蹤,但是這也表示module往往跟版本有很大的關係


在headers內定義了許多macro是供給modinfo用的

  • MODULE_LICENSE : 是使用GPL or BSD還是其他
  • MODULE_AUTHOR : 作者資訊
  • MODULE_VERSION : 版本,通常表示支援的kernel version等等
  • MODULE_DESCRIPTION : 描述
  • MODULE_ALIAS : alias name
  • MODULE_DEVICE_TABLE : 支援的device


有些關鍵字是C語言內沒有的,例如__init跟__exit,這是給compiler用的,分別表明該函數是初始化以及結束時該呼叫的,另外還有__initdata與__exitdata表示初始配置資料的函數跟離開時後歸還空間的函數,往往用於hotplug

其他關於編譯的細節以及一些版本號碼控管有空再說,本身這是一章節談論到很多關於kernel module的觀念

這本書每章節後面都會簡單的summary這章節的內容,蠻有幫助的


2012年3月20日 星期二

Linux驅動程式--chapter 1

玩嵌入式系統的人應該很難不動驅動程式有所牽連,不管是基於好奇也好,或者說萬般無奈為了某些功能也罷,驅動程式的確都是不可迴避的課題。講到這個,當然就屬於oreilly出版的linux驅動程式為一本入門的經典,接下來就是我拜讀的書本摘要,其實如果真的要鑽研linux驅動程式,這本書應該是放在你的架上的

驅動程式提供某些machanism,讓使用者依照某些policy來操作,所謂的machanism應該類似檔案操作的fopen, fwrite, fread, fclose,使用者要完成某項工作,例如讀取解析某些檔案,這些操作的程序比較類似policy。
linux將許多驅動程式變成了模組(module),好處是可以在隨時加入一些特性,主要依賴insmod與rmmod來引入與卸載模組。
模組大概可以依照硬體分成三類,character device、block device、queue,網路介面比較使用queue來對待
linux驅動程式強烈跟核心(kernel)相關,所以必須確定使用的核心版本,許多驅動程式在不同版本核心往往不保證相容性,因為kernel的API異動可能造成驅動程式的問題

因為現在已經新的開發大多已經使用linux 2.6以後的核心了,這也是這本書的第三版瞄準的目標,第二版是linux 2.4,2.4=>2.6架構上有很大的變動,所以舊版/二版的書基本上只能運作在2.4,要買書的讀者要注意