2009年11月9日 星期一

如何porting linux kernel給一顆新的ARM CPU

ARM CPU是目前使用最多的embedded CPU, 所以這邊會就我個人經驗來分享如何porting一顆新的ARM CPU Linux platform, 在porting之前有幾項硬體規格必須事先弄清楚:
  • CPU是何種ARM core, 例如arm926, 是V5, V6還是V7等等
  • SoC多少週邊元件在這顆Embedded ARM CPU之內
  • interrupt如何設定及使用, 這裏所指的是interrupt controller
  • timer如何設定及使用
  • DRAM & Flash mapping至CPU那個位置
收集這些資訊之後, 開始要做Linux kernel porting的工作, 以下是它的工作步驟
  • 修改源始碼根目錄的Makefile, 將其中幾個變數做修改如下
ARCH := ARM
CROSS_COMPILE := arm-linux-gnueabi
INSTALL_MOD_PATH := `pwd`/installed_modules
  • 在arch/arm目錄之下建立一個子目錄其名字為mach-開頭, 之後你可以取一個名字, 例如mach-victorcpu, 而在這目錄下的程式皆為HAL層的程式碼
  • 在arch/arm/tools目錄之下有一個文字檔 "mach-types" , 仿照其它CPU的寫法, 你必須定一個ID號碼, 必須是唯一的。
  • 在你建立的mach-victorcpu目錄建立Makefile & Kconfig, 並將你建立的Kconfig檔加入至arch/arm/Kconfig的include之中
  • 在新建mach-victorcpu的新目錄中, 必須幾個檔案而其內容如下:
  • MACHINE_DESCRIPTION
  • interrupt controller initialization
  • timer controller initialization
  • mapping fixed some SoC address to be virtual address, 有些SoC的元件在開機就必須使用到的, 就需要使用事先定對好的方式來設定, 例如timer
  • 若有PCI host, 則必須撰寫其HAL層在此, 而PCI BUS Driver則會在driver/pci的目錄中, 不用撰寫。
  • 若在cahce操作部若有和ARM的標準不同, 則必需撰寫其差異部分的程式在arch/arm/mm的目錄之下。
  • 接下來必需要開始撰寫各個SoC進來元件的device driver, 你要依照硬體的設計來撰寫個別device driver。
以上為porting Linux kernel時非常非常大項分法所需要撰寫的程式說明。

2009年10月29日 星期四

如何撰寫一個serial port的驅動程式

簡介

RS232是kernel中常用到的一種週邊元件,在以前主要是拿來連接終端設備的通訊介面,而今日則是常被拿來做控制或者和其它設備通訊的介面,因為其硬體簡單及成本低所以會被很多設備做為最基本的通訊介面,因為其發展有很長一段時間,所以在Linux中有為它特別規劃一組軟體堆疊,主要分為實體控制層以及上層的終端控制層,其架構如下:


















初始化
所以通常我們要寫的是實體層的驅動程式,在driver的進入點,若是PCI介面,則先註冊一個PCI的call back function,若是local bus則是可以註冊一個platform device call back function,當系統進入你所註冊的call back function,必須對你所使用的硬體介面位址做一個remapping的動作,就是將實體位址轉換成虛擬位址,接著後面的所有程式碼都要使用這個虛擬位址,這時要開始初始化你的硬體及driver,首先建立一個struct tty_driver的資料結構,其宣告如下:

struct tty_driver {
int magic; /* magic number for this structure */
struct cdev cdev;
struct module *owner;
const char *driver_name;
const char *name;
int name_base; /* offset of printed name */
int major; /* major device number */
int minor_start; /* start of minor device number */
int minor_num; /* number of *possible* devices */
int num; /* number of devices allocated */
short type; /* type of tty driver */
short subtype; /* subtype of tty driver */
struct ktermios init_termios; /* Initial termios */
int flags; /* tty driver flags */
int refcount; /* for loadable tty drivers */
struct proc_dir_entry *proc_entry; /* /proc fs entry */
struct tty_driver *other; /* only used for the PTY driver */

/*
* Pointer to the tty data structures
*/
struct tty_struct **ttys;
struct ktermios **termios;
struct ktermios **termios_locked;
void *driver_state; /* only used for the PTY driver */

/*
* Interface routines from the upper tty layer to the tty
* driver. Will be replaced with struct tty_operations.
*/
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
int (*write)(struct tty_struct * tty,
const unsigned char *buf, int count);
void (*put_char)(struct tty_struct *tty, unsigned char ch);
void (*flush_chars)(struct tty_struct *tty);
int (*write_room)(struct tty_struct *tty);
int (*chars_in_buffer)(struct tty_struct *tty);
int (*ioctl)(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
void (*throttle)(struct tty_struct * tty);
void (*unthrottle)(struct tty_struct * tty);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
void (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
int (*read_proc)(char *page, char **start, off_t off,
int count, int *eof, void *data);
int (*write_proc)(struct file *file, const char __user *buffer,
unsigned long count, void *data);
int (*tiocmget)(struct tty_struct *tty, struct file *file);
int (*tiocmset)(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);

struct list_head tty_drivers;
};

在這個資料結構中其中分為幾個部分,第一個部分是所以謂的callback function的註冊,在這些個callback function中你必須金對你有實現的部分給予註冊,若沒有就可以給予不註冊,但相對的你的驅動程式所支援的功能會較少。另一個部分則是驅動程式本身資訊的資料,如major number、starting minor number、tty device name等等,以下是一個例子:

struct tty_driver MySerialDriver;

#define MAX_PORTS 4

struct ktermios *my_termios[MAX_PORTS];

struct ktermios *my_termios_locked[MAX_PORTS];

memset(&MySerialDriver, 0 sizeof(MySerialDriver));

MySerialDriver.name = "ttyN";

MySerialDriver.major = 40;

MySerialDriver.minor_start = 0;

MySerialDriver.minor_num = MAX_PORTS;

MySerialDriver.num = MAX_PORTS;

MySerialDriver.magic = TTY_DRIVER_MAGIC;

MySerialDriver.type = TTY_DRIVER_TYPE_SERIAL;

MySerialDriver.subtype = SERAL_TYPE_NORMAL;

MySerialDriver.init_termios = tty_std_termios;

MySerialDriver.init_termios.c_cflag = B9600 CS8 CREAD CLOCAL;

MySerialDriver.flags = TTY_DRIVER_REAL_RAW;

MySerialDriver.termios = my_termios;

MySerialDriver.termios_locked = my_termios_locked;

MySerialDriver.open = my_open;


以下為driver initialize的工作內容

1. allocate tty driver資料結構使用

  • struct tty_driver *mxvar_sdriver=alloc_tty_driver(MXSER_PORTS + 1);
2. 在allocate的tty driver資料結構中填入必要的資訊

  • mxvar_sdriver->owner = THIS_MODULE;
  • mxvar_sdriver->magic = TTY_DRIVER_MAGIC;
  • mxvar_sdriver->name = "ttyMI";
  • mxvar_sdriver->major = ttymajor;
  • mxvar_sdriver->minor_start = 0;
  • mxvar_sdriver->num = MXSER_PORTS + 1;
  • mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL;
  • mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL;
  • mxvar_sdriver->init_termios = tty_std_termios;
  • mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
  • mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_DYNAMIC_DEV;

3. 設定tty會使用的各個call back function

  • tty_set_operations(mxvar_sdriver, &mxser_ops);
而其中的mxser_ops設定如下:
static const struct tty_operations mxser_ops = {
.open = mxser_open,
.close = mxser_close,
.write = mxser_write,
.put_char = mxser_put_char,
.flush_chars = mxser_flush_chars,
.write_room = mxser_write_room,
.chars_in_buffer = mxser_chars_in_buffer,
.flush_buffer = mxser_flush_buffer,
.ioctl = mxser_ioctl,
.throttle = mxser_throttle,
.unthrottle = mxser_unthrottle,
.set_termios = mxser_set_termios,
.stop = mxser_stop,
.start = mxser_start,
.hangup = mxser_hangup,
.break_ctl = mxser_rs_break,
.wait_until_sent = mxser_wait_until_sent,
.tiocmget = mxser_tiocmget,
.tiocmset = mxser_tiocmset,
};

4. 向tty_io註冊driver

  • retval = tty_register_driver(mxvar_sdriver);

5. 註冊 PCI client或者platform的driver, 等待其call back所設定probe程序

  • retval = pci_register_driver(&mxser_driver);
PCI client driver的資料結構如下:
static struct pci_driver mxser_driver = {
.name = "mxser",
.id_table = mxser_pcibrds,
.probe = mxser_probe,
.remove = __devexit_p(mxser_remove)
};

或者
  • retval = platform_register_driver(&mxser_driver);
platform client driver的資料結構如下:
static struct platform_driver moxaarm11_gmac_driver = {
.driver.name = DRV_NAME,
.probe = moxaarm11_gmac_probe,
.remove = moxaarm11_gmac_remove,

};

6. 這時HAL層(PCI or platform)若有找到該元件, 將會呼叫設定的probe call back function


probe call back function內做的事情

1. 取得所分配到的resource, 包含I/O及IRQ

2. 向kernel註冊所需的resource, 若是使用memory的方式來存取, 記得需要將取得的physical address用ioremap將其轉換為virtual address

3. 註冊IRQ service routine (ISR)

4. 針對每一個實體的port向上層tty做註冊

  • tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev);


remove call back function內做的事情

1. 將取得resource釋放出來

2. 將已remap過的virtual address做unremap的動作

3. 取消已註冊的ISR

4. 針對每一個已註冊的實體port向上層tty做取消的動作

  • tty_unregister_device(mxvar_sdriver, brd->idx + i);
原則上remove的動作剛好都和probe做相反的動作


各個operation call back function內做的事情
這些的call back function都會先經過tty_io的處理之後,才會再呼叫這些call back function。

open
這是當AP呼叫open()時會被呼叫的進入點,但是在呼叫你的open call back function之前,會先經過tty_io處理過,在你的open call back function中如何取得的minor number? 可以從tty_io傳給你的tty_struct structure 中的成員index取得(tty->index),你若有控制多port的serial port這時你就可以知道AP要open那一個port,另外linux是一個多工的作業系統,所以有可能多個AP會呼叫下來,也許會open同一個port,這時driver本身自已要記住該port已被open多少次,當是第一個open時需初始化該port,以及buffer的allocate,另外serial port的硬體設定是保留前一個設定,在open時不會做更動。

close
從call back的名字就可以知道,這是從AP的close()時的進入點,這個call back function應該檢查是否為最後一個close,也就之前已有多個open()之中的最後一個close(), 記得前面的open會記住被open幾次,在close則相對地減掉被open的數,在最後一次的close時,應將open中所取得的resource應全數release給kernel。

write
這是送資料的進入點, 通常我們都會先將資料放在自已的buffer, 之後再慢慢送, 而且通常會使用interrupt的方式來送, 而這個return值會是有多少bytes已存在你的buffer中

put_char
serial communication中有所謂software flow control, 它是用資料中兩個bytes XON(0x11), XOFF (0x13)來做這流量控制, 而當要送這個bytes時就會呼叫這一個call back function, 而且這兩個必須要馬上被送出, 不可放在buffer之中, 所以原則上會在這個call back function做一個flag記錄, 而在interrupt service routine中即時送出

flush_chars
這是清除本來在put_char中的資料, 若尚未送出將給予丟棄

write_room
檢查送的buffer還剩多少空間可以存放資料

char_in_buffer
已收進多少資料在driver buffer之中

ioctl
這是相對應AP的ioctl() API, 在linux的tty中已定義一些ioctl() command, 應給予支援, 你也可以自行定義一些ioctl()

set_termios
設定RS232如baud rate, data bits, stop bits等等, 將會使用struct termios來做設定資料結構

throttle
這是在做flow control用的, 在以往hardware不夠好的時候, flow control是軟體來實現, 而且由tty_io來控制, 現在硬體已比以前強了, 所以flow control部分將會由硬體來實現, 而這個將是停止送資料, 不用管是由software flow control or hardware flow control來的, 這個call back function被呼叫時, 就是停止送資料

unthrottle
這個動作也是在做flow control, 和上面的throttle相反, 則是開始送資料

stop
shutdown該UART port

start
啓動該port

hangup
這是在接modem時用的, 用在掛電話

break_ctrl
這是在送break訊號的控制

flush_buffer
這是清除在driver buffer中的資料, 將存放在buffer中尚未處理的資料全部給予丟棄

wait_until_sent
等待在buffer中尚未送出的資料全部送出, 而且需要block在這裏面

tiocmget
最得modem的訊號 CTS, DSR, DCD, RI

tiocmset
控制modem訊號 RTS, DTR

在了解各個call back function的定義及作業內容,這時寫driver的人就必須依照這樣的定義來撰寫serial port device driver. 最後最重要的是interrupt service routine, 原則上收送資料都是使用interrupt的方式來收送, 但是在送資料時, 應該是一次就將FIFO填滿, 而收的部分通常設定一個FIFO的high water就產生interrupt, 因為不可在FIFO滿的時候才產生interrupt, 這樣沒有緩衝時間來處理後面持續進來的資料, 容易造成FIFO overflow, 以上是RS232 device driver的大概說明, 其它還有許多細節的部分留待以後再說明

2009年10月28日 星期三

如何撰寫一個ethernet實體層的driver

ethernet現在已成為很多設備的主要通訊介面,因為它除了快速以及便宜之外,更可以讓它可以很容易架設網路,使多個設備可以互相通訊,這是網路通訊七層中最為底層的device driver, 它必須和硬體相關聯, 而現在的SoC中通常都會將這個通用元件SoC進來, 而如果你將這個driver完成將會讓網路的其它通訊堆疊馬上可以使用, 因以從Link層開始都會是屬於軟體的事情和硬體無關, 所以這部分也是Linux中最強最穩定的程式, 所以你就可以不用撰寫這部分的程式, 你只要專心地將ethernet完成即可。首先我們先來看網路的軟體架構:















以下為這個驅動程式的撰寫重點 :
  • 驅動程式的進入點, 則依照Linux驅動程式的標準寫法, 若是PCI bus則在初始化時依照PCI client驅程式的登錄方式, 若是SoC則依照platform driver的登錄方式。以下是一個範例說明:
static struct platform_driver victor_mac_driver = {
.driver.name = DRV_NAME,
.probe = victor_mac_probe,
.remove = victor_mac_remove,
};

static int __init victor_mac_module_init(void)
{
printk("%s \n",__FUNCTION__);
return platform_driver_register(&victor_mac_driver);
}
module_init(victor_mac_module_init);
  • 當你登錄完畢後則會進入probe的進入點, 在這裏我們需要初始化ethernet device driver。而在probe callback function中會做以下幾件事:
  1. 取得硬體資源 (memory & IRQ no), 並且將其做ioremap以取得virtual address
  2. 初始化硬體, 並註冊interrupt service routine
  3. allocate ethernet device structure, register a ethernet device
以下是範例程式:
static const struct net_device_ops victor_netdev_mac_ops = {
.ndo_init = victor_mac_init,
.ndo_open = victor_mac_open,
.ndo_stop = victor_mac_stop,
.ndo_start_xmit = victor_mac_start_xmit,
.ndo_get_stats = victor_mac_get_stats,
.ndo_set_multicast_list = victor_mac_set_mcast_list,
.ndo_do_ioctl = victor_mac_ioctl,
.ndo_change_mtu = eth_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
struct net_device *ndev;
struct resource *res;
ndev = alloc_etherdev(sizeof(victor_gmac_priv_t));
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ndev->netdev_ops = (struct net_device_ops *)&victor_netdev_mac_ops;
retval = register_netdev(ndev);

而以上在註冊ethernet device driver時會有幾個call back function需要給予撰寫, 其說明如下:

ndo_init
這是在註冊ethernet driver時馬上會被呼叫的call back function, 在此可以做一些初始化的動作

ndo_open
當使用者up該網路介面時會呼叫這個call back function, 通常會在此啓動該ethernet的硬體動作

ndo_stop
當使用者down該網路介面時會呼叫這個call back function, 通常會在此停止該ethernet的硬體動作

ndo_start_xmit
這是網路最後要將資料在硬體線路傳輸, 在這裏該driver不用對內容做任何更動, 而source MAC address通常是由硬體自動填入, 另外有一個很重要的事, 就是網路傳輸由開始者 (TCP or UDP) allocate skb_buff來儲存資料, 而在ethernet driver在送至硬體時需要free該skb_buff, 若沒有做這個動作將會由系統的記憶體越來越少

ndo_get_stats
由此來取得系統所定統計資料, 統計資籵必須由driver自行記錄, 並非由系統協助記錄

ndo_set_multicast_list
設定multicast MAC位址,驅動程式必須將這些位址設至硬體

ndo_do_ioctl
這是對於該元件的ioctl,通常一些運用程式會使用,如bridge程式就會使用,又如設定phy時也會使用。

ndo_change_mtu
設定網路的mtu值,通常是不會更動。

ndo_set_mac_address
設定該網路介面的MAC位址,通常MAC位址都是在初始化時讀取設定在硬體中,所以不會是任意更改,因為MAC位址是全世界上有所管制的,必須是唯一性。

ndo_validate_addr
設定validate MAC address

撰寫好以上各個call back function之後就是interrupt service routine, 而在interrupt service routine中就是要處理收和送的資料, 這部分和IC硬體有很大的關連, 隨著硬體而有所不同, 所以必須要好好地研讀硬體的軟體說明書.

2009年10月24日 星期六

如何撰寫在kernel的原始碼中的Kconfig

在整個的kernel source code中是一個非常有模組化的管理,而其中在2.6.x之後使用一種檔設定來管理選擇那些是要static link至kernel,而那些要是要模組化,而其檔名為Kconfig,其散佈在各個原始碼的目錄中,而在這個檔中將會描述各個模組的名稱及可以設定yes, no or module, 它也可以像c語言一樣可以include其它的Kconfig,當你在原始碼的目錄之下執行'make menuconfig'將可以看到以下的書面 :













其中有幾個基本的語法指令如下:
#用這個#開頭的這一行表示為註解,不會有任何作用

comment "這是選單上的說明文字, 無法選擇"

menu "這是一個選單的項目,你可以在定義你的選單名稱,將會和後面的endmenu成為一對"
source "這是引用另一個Kconfig的檔案"
........
endmenu

config USER_DEFINE_NAME #設定完之後將會使用這個名字在前面再加CONFIG_
bool "這個訊息將會被顯示出來,而前面的bool代表是為布林,表示這個選項只有Yes or No的選擇“
depend on OTHER_ITEM #這邊可以使用和C語言相同的邏輯運算,且在這個運算為真時這個選項才可以被選
select OTHER_ITEM #當這個選項USER_DEFINE_NAME為真時,OTTHER_ITEM也會同時被設定
default y #這是設定default value
--- help ---
這是說明文字,可有可無。

在以上bool的設定還有以下設定選項
tristate "這個選項會有三種可以,就是Yes, No or Module,多了一個選項為可以用模組的方式來編繹,所以這通常是在設定驅動程式時所用"
int "這應不難看出是在設定一個數值,可以和另一個設定range來檢查輸入所要的數值範圍是否正確"

以上是常用到的設定選項,其餘還有則較不常用,通常熟悉以上的選項就夠用了。
string "這是可以輸入字串的設定選項"

2009年10月19日 星期一

好用的除錯編碼

我們在寫程式難免會有需要除錯的時候, 但是要如何除錯, 使用IDE(整合式開發環境)是一個很好的選擇, 但是若沒有這樣的工具時, 這種現象在embdded的系統中是常碰到的情形, 以下則是我較常用的方式可供參考:

#define VICTOR_DEBUG
#ifdef VICTOR_DEBUG
#define dbg_printf(x, args...) printf("%s,%s,%d:" x, __FILE__, __FUNCTION__, __LINE__, ##args)
#else
#define dbg_printf(x, args...)
#endif

尤其是有很多程式碼時, 這會是一個較簡單又好用的方式, 其中__FILE__, __FUNCTION__, __LINE__都是編譯時編譯會自動幫你轉換的資訊, 如此一來你可以使用像printf的寫法, 而將一些除錯碼加入你的程式碼中, 另外我又用VICTOR_DEBUG的定義將其包圍, 如此一來, 當你不用時將那一行mark掉, 重新編譯就好了, 以下則是簡單的使用範例:

int main(int argc, char *argv[])
{
dbg_printf("\n");
........
........
function();
dbg_printf("\n");
.....
}

int function(int parm)
{
...........
..........
dbg_printf("\n");
.............
.........
dbg_printf("count=%d\n", count);
........
}

2009年10月18日 星期日

崁入式系統的基本硬體需求

崁入式系統通常是做為一種專用機所設計, 而所謂的專用機, 是指該設備只處理某件事事情而已, 不會像個人電腦一樣, 無法預測使用者將會做為何種運用。但是總括來講, 其中一定會有一個主要的運算元件, 也就是所謂的CPU, 即然為專用機, 必定有其所控制的設備, 也一定會透過某種通訊介面來做控制, 而常用的介面有RS232, ethernet, CAN bus等等, 因為專用機的功能特定, 也較為預估其所需要的運算能力, 但是通常是運算能力較低的需求, 另外現代積體電路的高度發展, 而能將CPU以及各種週邊元件通通做在一顆IC上, 因此我們便稱呼這種為SoC。另外光是有CPU, 通訊元件是不夠的, 要讓CPU運作需要有記憶體, 而記憶體又分為暫時記憶體(DRAM)及永久記憶體(Flash), 其主要目的是要來儲存程式用的, 具有CPU的設備都可以使用軟體(程式)來使用智能化,或者可以做較多的羅輯運算, 或者資料採取, 更進一步做分析的動作。

Linux原始碼存放架構

你可以在http://www.linux.org的網頁取得所有的Linux原始碼,網路上放的通常會是一個壓縮縮檔,當你取得之後你必須將其解壓縮在你將使用的開發平台上,而其中幾個較大的目錄解釋如下:
arch - 存放和CPU相關的基本原始碼,主要是和硬體有關,例如cache,mmu,interrupt等等
+- arm - ARM CPU相關的原始碼,以及用它做的的主板,在這個目錄之下所放的程式主要為HAL的程式碼
++- default - 這是放每一個平台的初始設定
++- kernel ﹣這是kernel程式的進入點,以及中斷程式的進入點
++- boot ﹣這是在build kernel時的link描述檔以及解壓縮的程式碼
++- mm ﹣ 這是記憶體處理的最底層程式,以及cache處理程式
++- lib ﹣ 這是一些公共的kernel library
++- match-xxxx - 這是各家平台的一些初始化程式
+- x86 - x86 CPU相關的原始碼
block - 一些區塊的驅動程式, 例如IDE, CD-ROM
crypto - 有關加解密的library,給kernel使用
Documentation - 存放各種說明文件
drivers - 各種週邊設備的驅動程式
+-char - 種字元型驅動程式, 或者需要tty_io處理的驅動程式
+-net - 網路底層的驅動程式, 泛指ethernet的實體層驅動程弍
+-usb - 存放USB host及常用的USB class device driver
+-mmc - MMC controller及MMC protocol的程式
+-pci - 此為PCI host (包含Card bus, PCI bridge, PCI host等等)
fs - 檔案系統,包含最上層的虛擬檔案系統,給使用者API呼叫使用
include - 所有的include file
init - kernel初始化的主要程式
ipc - kernel內部溝通訊息用
kernel - 主要是核心程式
lib - 在kernel中較常用的library
mm - 有關記憶體管理的程式碼
net - 和網路協定的相關原始碼,而且原則上和硬體無關,例如IPV4,TCP,UDP,ICMP等等
scripts - 在compile kernel時所使用的shell script, 以及menuconfig所用到的source code
security - 這是和加解密的kernel API source code, 以及軟體加解密引擎, 若有硬體支援加解密, 是放在driver/crypto
sound - 這是有關聲音的驅動程式
+-core
+-usb
+-pci
+-drivers
usr

什麼是Embedded Linux

什麼是Embedded Linux, 這是一個常常被首次接觸的人所問的問題, 要說明Embedded Linux之前先要了解什麼是Linux, 原則上Linux是一個從Unix所修改過來的OS, 而Unix是則是大型電腦主機所使用的一個OS系統, 而Unix則是一個多人多工的作業系統, 因為PC的日益強大, 所以便有人將Unix給予改寫使其可以在PC上使用, 更進一步由於PC的普遍而使有心人將其修改重寫, 並且免費提供使用, 以及開放其原始供大家修改, 讓它可以執行在各種CPU的電腦上。而embedded linux則是將其修改用在embedded的系統中, 因為embedded系統有OS的加入, 將可使embedded更強壯而可以做更多的事, 也因此使得embedded系統更智能化, 也讓embedded系統可以更容易開發及維護。
對於什麼是embedded系統, 也是有很多種說法, 而我們最簡單的說法則是一種專用機器, 而上是有CPU的運算能力, 而它不是一般的泛用型的機器,另外一種的簡單說法, 則是不是PC的其它都是embedded系統。

它通常會由以下幾個部分所組成:
  • SoC的CPU及內建DRAM & Flash
  • boot loader
  • linux kernel (included device drivers)
  • root file system
  • middle ware (daemon) (eg. apache, OpenSSH, iptables...)
  • application
而且在embedded linux中大部分是由C語言所,部分在boot loader & kernel booting會用到組合語言。而在開發embedded linux時你可能會需要以下各方面知識,或者一個具有以下能力的團隊來開發:
  • C language
  • Assembly language
  • 了解如何編譯cross tool chain (compiler & glibc)
  • boot loader
  • Linux kernel
  • linux device driver
  • 了解如何編譯各個middle ware source code by cross compiler
  • 了解Linux系統的開機順序
所以一個完整的embedded linux的開發者所需的知識很廣,而且要深,不然就需要將部分的軟件外包給其他人員。

2009年10月16日 星期五

我的主旨

Embedded Linux現在己被使用在很多地方, 它原先是來自standard Linux, 而原先是最早從大型電腦porting至x86機器, 而現在更是porting至ARM CPU, 可是當初的開發並未在X86的其它平台開發或測試過, 所以往往會很多問題產生, 甚至無法解決, 為了使其可以讓更多人容易上手, 於是將我多年的經驗在此分享給大家, 以期可以節省大家的時間。