2009年12月16日 星期三

SD driver with DMA support

有在研究 S3C24XX 平台的開發者應該會發現,SD 控制器驅動程式目前只支援 PIO mode,若嘗試將 DMA mode 的支援開啟,在偵測 SD 卡時就會發生問題。經過原始碼追蹤後我們可以發現到一些現象,且待我細細道來。

S3C24XX 平台的 DMA core 實作了要求佇列 (request queue) 的機制,使得它能夠接收多個 DMA 傳輸要求,並設定相關的暫存器來進行傳輸。當設定完成後,我們就可以設定 SD 控制器來觸發 DMA 控制器進行實際的資料傳輸。當要求佇列中有新的傳輸要求存在時,DMA core 會檢查 DMA 控制器是否已經在執行目前的要求;若已在執行,DMA core 就會取出新的要求,並將來源位址、目的位址以及資料長度等參數寫入暫存器中 (對暫存器寫入並不會對進行中的傳輸產生影響)。若否,DMA core 會繼續等待直到逾時為止。換言之,除非 DMA 控制器起始傳輸,否則 DMA core 是不會處理新的傳輸要求的。

MMC core 在讀取資料的過程中,預設會使用 scatter-gather DMA 來提高傳輸效率,或避免向核心要求一大塊連續的記憶體空間;然而 S3C24XX DMA 控制器並不支援此種傳輸方式,因此在執行時我們就會發現到,由於佇列中還有傳輸要求尚未設定,DMA core 不斷地在檢查 DMA 控制器是否正在執行;但是在佇列中的所有傳輸要求都被設定完成之前,SD 控制器是不會觸發 DMA 控制器來起始傳輸的 (雞生蛋,蛋生雞?),於是我們就會看到 DMA core 一直在 busy waiting,直到 time out 為止。

因此要解決這樣的問題,在向 MMC core 註冊 host controller 時,要設定該控制器同時間只能接受一個 DMA 傳輸要求 (請見 struct mmc_host 中的 max_hw_segs 及 max_phys_segs 欄位)。同時也可開啟 bounce buffer 支援以提高傳輸效率。

2009年10月28日 星期三

Samsung touchscreen driver issue

三星的主流核心支援其實已存在相當久,幾乎可說是所有 ARM-based SoC 中支援最完整的。但經常關心 ARM Linux kernel 發展的網友也許會發現到,怎麼核心中每項周邊都有驅動程式支援,卻獨獨缺了觸控面板?

其實相關的 patch 早已在各個專案中使用 (如 OpenMoko),但由於都是直接操作暫存器,而非架構在 Ben Dooks 的 adc driver 之上(詳見之前的文章),是故一直沒有被整合進去。今年四月初 Nelson Castillo 將原來在 OpenMoko 使用的驅動程式移植至主流核心,並使用 adc driver 來做取樣的工作。雖然已有作為先期準備的 patch 被整合,但直到目前為止完整的驅動程式仍未進行合併。

最近關於這個議題又有了新的爭論。由於 Nelson 的版本引進了多層次的濾波器(filter)來去除不合法的取樣點,Russell King 認為這違背了 policy-mechanism 分離的原則;所有的取樣點應該原封不動地往上傳遞至 user-space(tslib)來做處理,在驅動程式中使用 filter 是缺乏彈性的作法(附帶一提,Russell 是 tslib 的作者)。但 Nelson 認為,在驅動程式中,filter 可以根據需要,透過回傳值來要求 adc driver 多抓一些取樣點來作判斷;而這是 user-space 所無法達成的優點。且這樣的設計方式可以解決在電阻式面板所遇到的問題(在觸控筆按下和釋放的瞬間,會有大量的雜訊出現)。

就我個人測試過的經驗,Nelson 的 patch 確實比單純只使用 tslib,觸控板的表現要好得多,筆觸變得比較順暢,且抖動的情況減少很多。雖然在整合進 board-dependent 的程式碼時相對比較麻煩,不過其效果似乎是值得這麼做的。

2009年10月13日 星期二

雜記

最近這幾個月來因為在做新專案的關係,比較沒時間上來寫寫東西;而 Linux kernel 方面也只 post 了幾個小修正 (因為最近突然發現我所維護的開發板支援無法編譯成功),如此而已。目前三星上的 Linux 開發主力都轉往 S3C64XX 及新的 S5P 系列,因此 S3C24XX 這邊似乎已經沒有什麼好修的了。也許現在是時候去研究其他的子系統了。

2009年8月30日 星期日

Samsung 將釋出 Linux 原始碼

昨天收信時看到了這封信件,以及 Harald Welte 承諾將在三星採用社群開發模式上提供協助。一直以來,S3C24XX 及 S3C64XX 系列的核心支援,包含原始碼的目錄架構、驅動程式,以及各種修正,大部分都是由 Ben Dooks 所完成,也因為他所貢獻的大量基礎建設,使得像我這樣的開發者能夠相當容易地將各種 Samsung-based 開發板的支援加入 Linux 核心之中。如今三星這間半導體大廠願意投入人力來共襄盛舉,若三星自已維護的 kernel tree 能夠順利合併進入主流版本,那麼我們將可以直接自主流核心就得到官方的支援。有了大公司的加持,相信未來 Linux 對於三星 SoC 的支援完整度及執行效率上會得到大幅度的增進。對我們這些使用三星 SoC 的開發者來說,自然是再好不過了。

當然,聽其言,觀其行,我們還是要持續注意三星的實際作為才行。
PS. 有興趣的人可參考這裡所提,下載目前三星所釋出的 kernel tree.

2009年7月29日 星期三

Linux gpiolib

GPIO 在嵌入式系統中是相當常見的 I/O 裝置,全名為 General Purpose I/O。由於 GPIO 針腳可被個別設定和輸出訊號,因此我們可以利用 GPIO 來驅動 LED、或者實作低速的 I/O 通訊協定(如 I2C、SPI)。也可以利用 GPIO 來做為外部訊號的輸入端,在訊號觸發時,驅動程式中可使用 polling 或中斷方式來獲得通知。在嵌入式 SoC 平台上通常配有大量的 GPIO,且多半會和其他週邊裝置共用針腳,因此在使用時要特別注意,以免發生衝突。

過去在 Linux 上的 GPIO 程式碼大部分是屬平台相依的程式碼,因此 API 的實作也是由各平台的開發者自行設計,但除了 SoC 內建的 GPIO 外,系統設計者也會額外以 FPGA 或 I/O expander 來擴充 GPIO 的數量;因此要設計一種方式,除了能夠使用內建的 GPIO 外,也能使用這些額外的 GPIO,並透過一個統一的介面來存取它們。

gpiolib 的引入為上述的問題提供了一個解決方式。gpiolib 將每個 GPIO 的來源 (SoC、FPGA,或是 I/O expander) 用 gpio_chip 結構來表示,開發者可以定義其 GPIO 的編號、數量,以及相關的回呼函式 (callback function)。開發者需要為這些 gpio_chip 結構定義不同的回呼函式來處理不同的 GPIO 來源。如此一來,使用者只需指定 GPIO 編號,gpiolib 會替我們尋找對應的 GPIO 來源,並呼叫適合的函式來存取它,非常地方便。

PS. 需要注意的是,某些 GPIO 來源(如 I2C 介面的 I/O expander)存取時可能會進入睡眠狀態,因此不能夠使用於 ISR 或 tasklet 等不可睡眠的函式之中。

2009年6月17日 星期三

Kernel API always changes

最近因為工作上的需要,花了些時間學習如何撰寫 Linux 區塊裝置驅動程式,以及了解 Linux block subsystem 的架構。我主要參考兩本書藉,其中一本正是大名鼎鼎的 Linux Device Drivers 3rd (LDD3);而另外一本則是去年才出版的書藉 Essential Linux Device Drivers

我嘗試依照書本上的內容寫了一個 RAM-based 的區塊驅動程式,但在我的電腦上卻因為參考不到函式呼叫而無法成功編譯 (核心版本 2.6.28)。原因當然是因為核心 API 的改變,正如許多資深開發者所言,由於開發的需要,核心 API 總是不斷地在變化,因此若不將程式碼合併回主流版本,後續的維護將會非常麻煩。

以 LDD3 來說,其所列的範例程式片段如下:

sectors_xferred = sbull_xfer_request(dev, req);
if (! end_that_request_first(req, 1, sectors_xferred)) {
    blkdev_dequeue_request(req);
    end_that_request_last(req);
}

請注意 LDD3 所依據的核心版本為 2.6.10,到了 2.6.28,這些函式已被移除,取而代之的是:

sectors_xferred = sbull_xfer_request(dev, req);
__blk_end_request(req, 0, sectors_xferred << 9);

由於 __blk_end_request() 函式的第三個參數為成功傳輸的位元組 (byte),而非區段 (sector),所以需做單位轉換。另外可能會造成混淆的地方是 block subsystem 也提供另一個函式 blk_end_request(),兩者功能相同,差別在於後者是有加上 spin lock 的版本;不過由於核心在執行上述的程式碼之前已先行上鎖了,是故就不需要再上鎖一次了。

由以上範例,我們可以了解到,核心 API 的改變對於驅動程式的維護而言,並不僅僅是舊函式的消滅或新函式的加入而已,我們還需要評估應如何修改程式,以及修改後是否會影響程式的行為。通常核心開發者會負責將所有相關的驅動程式修改到好,但若是 out of tree 的程式碼,這部分可就得自己傷腦筋了。

2009年6月8日 星期一

移植 Ångström

因為看到另一個基於 OpenEmbedded 的 distribution Ångström,覺得它比起 Poky 來說提供更多的套件選擇,且由於 Ångström 的目錄架構和 OE 相同,所以在 OE 下新增的套件,馬上就可以套用在 Ångström 之上,而不需做任何修正。是故就決定以 Ångström 做為我移植軟體至開發板的基礎開發套件。

於是上個月的工作就是將原來寫給 Poky 用的設定檔修改以後再移到 Ångström 的目錄之下,然後再修改 Ångström 下的 linux recipe 來支援我目前使用的開發板,接著測試完成後,再提交至 OE mailing list。大功告成。

2009年5月13日 星期三

S3C24xx ADC driver (2)

第一篇文章中我們提到,adc driver 簡化了一部分硬體操作的細節,並提供 API 讓驅動程式使用。但就 touchscreen 驅動程式開發者的立場來看,它仍然有一些問題(以下純屬個人意見,僅供參考):
  • 雖然它給予驅動程式開發者在擷取數據上的方便性,但並未處理與觸控板相關的硬體操作,故開發者仍必須自行設定相關的暫存器來處理按下及釋放的事件;由於 ADC 硬體本身就額外支援觸控板的功能,adc driver 做為硬體與 touchscreen driver 間的中介層,應儘量隱藏硬體的細節,並提供完整的介面供上層驅動程式使用,而如今開發者除了須自行設定暫存器外,還必須先查看 adc driver 的原始碼,了解它如何設定暫存器,以避免自己的程式碼去干擾 adc driver 的運作,似乎失去了資訊隱藏的意義。
  • 這點比較見人見智;一般來說,驅動程式在呼叫 ioremap() 之前,會先呼叫 request_mem_region() 來防止其他驅動程式存取特定的暫存器群。但為了讓 touchscreen driver 也能夠存取暫存器,adc driver 允許 touchscreen driver 映射該區塊,於是變成兩個驅動程式存取同一組暫存器群的局面。我是不知道其他人怎麼想,但我就是覺得很怪,其中一部分程式碼若不小心更新了不該更新的暫存器,那是有可能會影響到另一個驅動程式的運作,造成潛在的錯誤。
基於上述兩項理由,我曾徵詢是否可將觸控板的功能也納入 adc driver 之中,也弄了一個 patch,不過 Ben 認為應該要維持程式碼的簡潔,因此否決了這個提議。

也許以後會有所改變吧,不過,誰知道呢 :)

2009年4月3日 星期五

S3C24xx ADC driver

上個月花了相當多的時間在研究 S3C24xx 的觸控面板控制器和驅動程式,在這邊稍微分享一下心得,並介紹目前在 mainstream kernel 所實做的 ADC driver core。

S3C24xx 的 ADC 控制器共有 8 個 channel,其中有四個 channel 可設定來接收觸控螢幕的訊號,並提供多種轉換模式,例如手動轉換 x 及 y 座標,或是讓控制器自動轉換等等。同時控制器也可偵測觸控筆「按下」或「釋放」的動作。但是需注意的是,同一時間只能有一種模式可以運作;換言之,驅動程式必須適時地在以下三種模式間切換:
  • 偵測「按下」的動作
  • 偵測「釋放」的動作
  • 取樣並轉換 x 及 y 座標
設定不同的模式皆需要對暫存器 (ADCTSC) 寫入不同的值。

除了被用做接收觸控訊號的四個 channel 外,剩下的另外四個則可連接其他的感測器。過去分散於各個專案中的觸控驅動程式 (截至目前尚未整合進入 mainstream),都是直接操作控制器,因而多出的四個 channel 即便接上了裝置,該驅動程式也無法與 touchscreen driver 共存 (不同的 driver 存取同一組暫存器會發生 race condition 的問題)。因此為了讓這些不同裝置的驅動程式能夠共用該控制器,Ben Dooks 在 2.6.28 引入了 ADC driver core (以下簡稱 adc driver),定義了一組介面,讓上層驅動程式能夠透過該介面來使用控制器。

若要使用 adc driver,驅動程式需要定義自己的回呼函式 (callback function),並向它註冊,註冊函式會回傳一個型態為 s3c_adc_client 的指標,其後的函式呼叫則以該指標做為第一個引數。adc driver 內部維護了一個要求佇列來紀錄來自外部的服務需求,並給予觸控有關的需求最高的優先權,以便可即時回應使用者的操作。在運作過程中,adc driver 會不斷地呼叫先前所註冊的 callback 以通知驅動程式處理取樣的數據,次數則可在要求服務時以參數設定。

對觸控驅動程式的開發者來說,adc driver 簡化了一部分複雜的細節,讓開發者透過函式介面來要求服務,而不必操作底層的暫存器。但我必須指出,它只是簡化了「一部分」,並非全部;至於為何,我們留待下一篇文章來探討。

2009年2月22日 星期日

Poky porting (2)

之前能夠以核心 2.6.29-rc4 成功編譯出的 task 為 poky-image-minimal,最近這兩天開始嘗試執行 poky-image-core,兩者的差別在於 minimal 只編譯了最少量的套件,只求可以開機就行;而 core image 則包括了完整的 GNOME Mobile 環境,也因此所需的編譯時間更久,也陸陸續續地遇到一些問題,其中比較大的問題主要是在核心模組的編譯之上。

怎麼說呢?目前所使用的版本為 Pinky-3.1.1 (08年六月釋出),如果沒記錯的話,那時的核心大約是在 2.6.25 rc 階段。不過自 2.6.28 版以後,核心引頭檔的目錄架構出現了大變動:原來在 include/asm-*/ 之下的引頭檔搬移至 arch/*/include、arch/*/mach-*/include 及 arch/*/plat-*/include 之下,並取消了 symbolic link 的使用,全由 makefile 來決定要引用的目錄。因此問題就來了,用來編譯舊版核心的 bb 檔只需要複製 include/asm-*/ 下的引頭檔至 staging 目錄下即可;但到 2.6.28 版以後,引頭檔的路徑已經改變,故需要做特別的處理。因此在 Pinky-3.1.1 下,編譯完核心後,還得再手動複製必要的引頭檔至正確的路徑之下,後續的核心模組編譯才能正確執行。

不過看了一下 Poky git 中,最新的核心版本是 moblin 的 2.6.29-rc2,不確定能不能正確處理,嗯嗯…

PS.
用 uclibc 要編譯 poky-image-core 的套件還是會遇到許多問題,且都是編譯過程的錯誤;想要順利完成的話,建議還是用 glibc 比較好。

2009年2月17日 星期二

Poky porting

最近這幾天,我一邊研究 OpenEmbedded building system 的目錄結構及設定檔撰寫,一邊嘗試利用 Poky 來編譯目前最新穩定版本 (2.6.28) 的核心。原因無他,主要是 2.6.27 以後的核心才有支援我現在使用的開發板。是故整個過程可分為兩階段,一是增加新版本核心用的 bb 檔案,另一項則為增加新硬體的設定檔。

要加入新版本核心其實可以將 meta/packages/linux/ 下的 bb 檔找一個你覺得適合的複製一份來改,內容大同小異,操作如下:

cd meta/packages/linux
vi linux-<MACHINE>_2.6.28.bb
# 填入自訂的內容,或複製現有的來修改
...

要注意的是,我們還需要再新建一個目錄,名稱如下所示,並複製核心使用的組態檔到該目錄下,檔名隨意,方便記憶即可:

mkdir linux-<MACHINE>
cp ~/defconfig ./defconfig-2.6.28

接著在修改 bb 檔時,幾個重要的變數需要設定:
  • PV: 套件版號,OpenEmbedded 會利用該變數來決定使用那個版本來編譯,預設是找最新的版本。
  • S: 套件原始碼目錄,也就是 tarball 解開後放置的路徑,在這個例子中是 ${WORKDIR}/linux-2.6.28
再來就是在 bb 檔中撰寫上 patch 以及複製核心組態檔用的 shell script,這部分可以參考其他 bb 檔的做法,在此就不贅述。

加入新硬體的設定檔可參考 meta/conf/machine/ 下的檔案來修改,由於我的開發板和 OpenMoko 所使用的硬體是同屬於 S3C2440 的晶片,是故就直接複製 fic-gta01.conf 來修改:

cd meta/conf/machine
cp fic-gta01.conf <MACHINE>.conf

修改的部分如下:
  • MACHINE_FEATURES = "kernel26 vfat ext2"
  • MACHINE_DISPLAY_WIDTH_PIXELS = "800"
  • MACHINE_DISPLAY_HEIGHT_PIXELS = "480"
  • PREFERRED_PROVIDER_virtual/kernel = "linux-<MACHINE>"
  • 移除 MACHINE_CLASS
  • 移除 MACHINE_EXTRA_RDEPENDS, MACHINE_EXTRA_RRECOMMENDS
  • 移除 EXTRA_IMAGECMD_JFFS2
  • 移除 SERIAL_CONSOLE
  • 移除 EXTRA_IMAGEDEPENDS
其餘照舊。

修改完成後,在 build 目錄下 (別忘了要 source poky-init-build-env),鍵入以下的指令:

bitbake linux-<MACHINE>

就會開始進行編譯程序。若發生錯誤,查看 build/tmp/work/<MACHINE>-poky-linux-gnueabi/linux-<MACHINE>.../temp/ 下的紀錄檔並修正。

目前已經加入了 2.6.28 及 2.6.29-rc4 的 recipe。其中 2.6.29-rc4 包含了開發板上 SD 控制器LCD 控制器的支援,相信不久應該就可以看到跑 GNOME Mobile 的樣子了。

2009年2月13日 星期五

Poky Linux 建構系統使用感想

最近為了要建立一個完整可供核心掛載的檔案系統,去玩一玩了 Poky 這個嵌入式套件。無可否認的,它所根基的 OpenEmbedded 是個很有彈性的套件建構系統,在已支援的開發板上,只要改一些設定,就可從無到有建構核心及所需的檔案系統映象 (file system image),並且會幫你處理套件間相依性的問題,也可以只建構特定套件,打包成 ipkg 或 deb 檔以供其他系統使用。有過土法煉鋼、自己抓原始碼回來編譯的痛苦經驗的人,就會了解這個功能是多麼地令人叫好。

雖然功能強大,但若要加入新的開發板的支援,或是要解決某個套件不能順利編譯的問題,你就會發現,它的設定檔也是異常的複雜難懂;每個設定檔會去引用其他共享的檔案,並且預先設定一大堆變數。有些變數是在目前檔案被設定,有些則是在被引用時才設定。而被引用的檔案又會去引用其他的檔案……沒完沒了。

就在不久前,uClibc 發生編譯錯誤 (poky 3.1.1),從錯誤訊息看來,發現是硬體架構設定錯誤所致。但找了老半天,就是不知道應該要改那個設定檔,令人懊惱。花了一個下午的時間,才找到問題的關鍵,修正後,這才能順利編譯。我另外把它做成了一個 patch,但我沒有網頁空間可以放,有相同問題的朋友可以來找我要,或有朋友願意提供他的空間也成。

延伸閱讀:
Embedded Linux 系統性的教學看法
Embedded Linux 應用的痛處: OpenEmbedded