2008年12月31日 星期三

X86上執行Android[轉貼]

Lim,GeunSik剛剛在android-porting討論組中詳細闡述了這個方法。我先把方法放過來,不過還沒來得及驗證,他用的是ASUS Eee,估計效果和N810上的差不多的。一下所有步驟都是基于Fedora的,ubuntu用戶自行參考。

開發環境:
- Linux PC: Fedora 9 ( 2.6.25.6-55.fc9 i686)
. CPU: Intel(R) Core(TM)2 Duo CPU T5750 @ 2.00GHz ( Samsung
SENS R60 Laptop )
. RAM: Samsung DDR Ram
- Target: Eee PC (ASUS) and Samsung nettop NC01
- Reference: http://www.kandroid.org/board/board.php?board=androidsource&command=body&no=19

1. Linux發行版訊息

首先是驗證系統訊息

Fedora9$> uname -a
Linux fedora-invain9 2.6.25.6-55.fc9.i686 #1 SMP Tue Jun 10 16:27:49
EDT 2008 i686 i686 i386 GNU/Linux

Fedora9$> gcc –version and Fedora9$> gcc34 –version
Fedora9$> gcc –version
gcc (GCC) 4.3.0 20080428 (Red Hat 4.3.0-8)
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There
is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

Fedora9$> gcc34 –version
gcc34 (GCC) 3.4.6 20060404 (Red Hat 3.4.6-9)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There
is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE


2. repo init 和 Sync 操作
- 首先下載Android for x86的完整源代碼。Eee pc 的 dev tree 在 “git://android.git.kernel.org/platform/vendor/asus/eee_701.git”.

Fedora9$> cd ~
Fedora9$> mkdir bin_x86 && cd bin_x86
Fedora9$> mkdir mydroid && cd mydroid
Fedora9$> repo init -u git://android.git.kernel.org/platform/manifest.git
-b cupcake
Fedora9$> repo sync
Fedora9$> vi ./.repo/local_manifest.xml





Fedora9$> repo sync
… A new repo command ( 1.8) is available.
… You should upgrade soon:

cp /home/invain/bin_x86/mydroid/.repo/repo/repo /home/invain/bin/
repo

Initializing project platform/vendor/asus/eee_701 …
remote: Counting objects: 33, done.
remote: Compressing objects: 100% (31/31), done.
remote: Total 33 (delta 2), reused 33 (delta 2)
Unpacking objects: 100% (33/33), done.
From git://android.git.kernel.org/platform/vendor/asus/eee_701
* [new branch] cupcake -> korg/cupcake
* [new branch] master -> korg/master

3. 構建 x86 android

Fedora9$> export PATH=$PATH:/usr/sbin:/sbin (to use /sbin/tune2fs
command of e2fsprogs pack )
Fedora9$> TARGET_ARCH=x86 TARGET_PRODUCT=eee_701 DISABLE_DEXPREOPT=true CC=gcc34 CXX=g++34 make -j2 installer_img

build/core/product_config.mk:207: WARNING: adding test OTA key
============================================
TARGET_PRODUCT=eee_701
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_ARCH=x86
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=
============================================
build/core/main.mk:178: implicitly installing apns-conf_sdk.xml
…………… Below Omission ……………….


* 在構建中可能遇到一些問題,解決辦法是

- 用低版本gcc 替換 gcc-4.3 version. Ubuntu 8.04LT用的是GCC 4.2, Ubuntu 8.10是GCC 4.3,所以推荐用8.04來做build

- 如果你正在使用 GCC 4.3, 請照下面修改源代碼中的相關頭文件:

Fedora9$> vi external/srec/tools/thirdparty/OpenFst/fst/lib/../../fst/
lib/vector-fst.h
Fedora9$> vi external/srec/tools/thirdparty/OpenFst/fst/lib/symbol-
table.cpp
Fedora9$> vi frameworks/base/tools/aidl/aidl.cpp –> #include
, #include

等等。

- Lim,GeunSik使用的是gcc 3.4

#> yum install compat-gcc*
#> rpm -qa grep compat-gcc
compat-gcc-34-3.4.6-9.i386
compat-gcc-34-c++-3.4.6-9.i386
compat-gcc-34-g77-3.4.6-9.i386

Fedora9$> vi

Fedora9$> ls -lh out/target/product/eee_701/

total 770624
drwxrwxr-x 9 invain invain 4096 2008-12-24 16:05 .
drwxrwxr-x 3 invain invain 4096 2008-12-24 15:34 ..
-rw-rw-r– 1 invain invain 2606080 2008-12-24 15:50 boot.img
-rw-rw-r– 1 invain invain 57 2008-12-24 16:03 clean_steps.mk
drwxrwxr-x 4 invain invain 4096 2008-12-24 15:54 data
drwxrwxr-x 2 invain invain 4096 2008-12-24 15:50 grub
drwxrwxr-x 4 invain invain 4096 2008-12-24 16:05 installer
-rw-rw-r– 1 invain invain 406862848 2008-12-24 16:06 installer.img
-rw-rw-r– 1 invain invain 1951340 2008-12-23 19:18 kernel
drwxrwxr-x 12 invain invain 4096 2008-12-24 16:05 obj
-rw-rw-r– 1 invain invain 607384 2008-12-24 15:50 ramdisk.img
drwxrwxr-x 9 invain invain 4096 2008-12-24 15:50 root
drwxrwxr-x 4 invain invain 4096 2008-12-24 15:50 symbols
drwxrwxr-x 12 invain invain 4096 2008-12-24 15:53 system
-rw-rw-r– 1 invain invain 372056064 2008-12-24 16:05 system.img
-rw-rw-r– 1 invain invain 5156864 2008-12-24 16:04 userdata.img

Fedora9$> file out/target/product/eee_701/installer.img
./out/target/product/eee_701/installer.img: x86 boot sector; GRand
Unified Bootloader, stage1 version 0×3; partition 1: ID=0×83, active,
starthead 0, startsector 2048, 8878 sectors; partition 2: ID=0×83,
starthead 0, startsector 10926, 783728 sectors

Fedora9$> file out/target/product/eee_701/installer.img
File: `./out/target/product/eee_701/installer.img’
Size: 406862848 Blocks: 793552 IO Block: 4096 일반 파일
Device: fd00h/64768d Inode: 5349917 Links: 1
Access: (0664/-rw-rw-r–) Uid: ( 778/ invain) Gid: ( 778/
invain)
Access: 2008-12-24 16:08:21.000000000 +0900
Modify: 2008-12-24 16:06:05.000000000 +0900
Change: 2008-12-24 16:06:05.000000000 +0900

Fedora9$> file out/target/product/eee_701/system.img
./out/target/product/eee_701/system.img: Linux rev 0.0 ext2 filesystem
data

Fedora9$> file out/target/product/eee_701/userdata.img
./out/target/product/eee_701/userdata.img: Linux rev 0.0 ext2
filesystem data

Fedora9$> pushd out/target/product/eee_701/

Fedora9$> sudo mount -o loop boot.img /mnt

Fedora9#> popd
total 2519
-rw-rw-r– 1 invain invain 77 2008-12-24 15:50 cmdline
-rw-rw-r– 1 invain invain 1951340 2008-12-24 15:50 kernel
-rw-rw-r– 1 invain invain 607384 2008-12-24 15:50 ramdisk

Fedora9$> cat /mnt/test/cmndline
console=tty0 console=ttyS1,115200n8 console=tty0
androidboot.hardware=eee_701

Fedora9$> cp /mnt/test/ramdisk /tmp/ramdisk.gz
Fedora9$> pushd /tmp
Fedora9$> gunzip ramdisk.gz
Fedora9$> cpio -iv < ramdisk

Fedora9$> popd
init.goldfish.rc
init
data
init.eee_701.rc
proc
sbin
sbin/adbd
system
init.rc
default.prop
lib
lib/modules
lib/modules/atl2.ko
lib/modules/drm.ko
lib/modules/fbcon.ko
lib/modules/cfbimgblt.ko
lib/modules/i915.ko
lib/modules/cfbcopyarea.ko
lib/modules/bitblit.ko
lib/modules/softcursor.ko
lib/modules/font.ko
lib/modules/cfbfillrect.ko
dev
sys
2958 blocks

Fedora9$> file /tmp/init
/tmp/init: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
statically linked, not stripped


4. 制作USB Pen Disk安裝包

- 在BIOS中把第一啟動設備設置成USB,然後

Fedora9$> dd if=out/target/product/eee_701/installer.img of=/dev/your_usb_disk(例如/dev/sda1)

好了,接下來可以用你的android USB Pen Disk來嘗試啟動了。Enjoy it。

最後友情提示一句,我還沒有試這種方法,不過反正也不會破壞什麼,有興趣的朋友可以試一下。

資料來源:
http://www.forwind.cn/2008/12/24/android-on-x8/
Howto build Android full source for X86 Architecture like EeePC(ASUS)

SystemC Modules and Processes(2)

階層架構(Hierarchy)
一個EXOR閘是由4個NAND閘(instance)組成。要達成這個目的,是在EXOR閘的建構式裏去連結實體化的NAND閘。
底下列出EXOR閘的範例程式:

#include "systemc.h"
#include "nand2.h"
SC_MODULE(exor2)
{
sc_in<bool> A, B;
sc_out<bool> F;

nand2 n1, n2, n3, n4;

sc_signal<bool> S1, S2, S3;

SC_CTOR(exor2) : n1("N1"), n2("N2"), n3("N3"), n4("N4")
{
n1.A(A);
n1.B(B);
n1.F(S1);

n2.A(A);
n2.B(S1);
n2.F(S2);

n3.A(S1);
n3.B(B);
n3.F(S3);

n4.A(S2);
n4.B(S3);
n4.F(F);
}
};

程式說明:
這個程式看起來跟NAND閘的程式很像,但注意它有去include nand2.h的檔案,這是為了去使用NAND閘模組所必需的。

這個exor2模組被建立起來,並宣告這個模組的ports。請再注意一下,設計上是可以重複使用A,B及F的名字,因為它跟nand的層級是不同的。

在電路圖中你可以看到有許多的線用以連結每個NAND閘。而在SystemC裏線的表現方式則是用sc_signal S1,S2及S3。
sc_signal是一個類別(class),它包含了樣版參數(template parameter)用以定義資料的型態,在這個例子中是使用bool的資料型態。
sc_signal是一個SystemC內建的通道(primitive channel)類別,內建的通道當然在SystemC class library裏。它的行為就如同VHDL裏的signal。

EXOR閘的建構式會比NAND閘再複雜一點,它必需實體化4個nand2來用。在port的定義之後,緊接著宣告實體化4個nand2:n1,n2,n3,n4。
每個實體化的nand2都必需要有個名字,所以我們在exor2建構式初使化時傳了4個"N1","N2","N3","N4"的名字,分別給n1,n2,n3,n4這4個nand2。

最後,將所有的ports所需要的連接線接起來。這樣子就完成了這個建構式了。

測試程式(Test bench)
要測試這個設計,當然就需要測試訊號的產生程式。這就必需要再寫另一個module,跟前面寫的module很類似。
但比較不同的是,在這裏我們是使用執行緒(thread)SC_THREAD,這個行程會在需要時進入休眠狀態。
底下列出這個Test bench的範例程式stim.h


#include "systemc.h"
SC_MODULE(stim)
{
sc_out<bool> A, B;
sc_in<bool> Clk;

void StimGen()
{
A.write(false);
B.write(false);
wait();
A.write(false);
B.write(true);
wait();
A.write(true);
B.write(false);
wait();
A.write(true);
B.write(true);
wait();
sc_stop();
}
SC_CTOR(stim)
{
SC_THREAD(StimGen);
sensitive << Clk.pos();
}
};

程式說明:
這裏要注意的是最後它們呼叫sc_stop(),這個函式會讓整個模擬結束。

有了訊號產生程式後,我們還需要一個監控程式,用來監控我們想看到的訊號改變情況,這個程式相當簡單,我們先忽略這個部份,在這個範例裏監控程式檔名是mon.h

底下列出top level的程式碼,它是寫在main.cpp裏,當然main.cpp必需include之前所寫的那些子模組(submodules)

#include "systemc.h"
#include "stim.h"
#include "exor2.h"
#include "mon.h"

int sc_main(int argc, char* argv[])
{
sc_signal<bool> ASig, BSig, FSig;
sc_clock TestClk("TestClock", 10, SC_NS,0.5);

stim Stim1("Stimulus");
Stim1.A(ASig);
Stim1.B(BSig);
Stim1.Clk(TestClk);

exor2 DUT("exor2");
DUT.A(ASig);
DUT.B(BSig);
DUT.F(FSig);

mon Monitor1("Monitor");
Monitor1.A(ASig);
Monitor1.B(BSig);
Monitor1.F(FSig);
Monitor1.Clk(TestClk);

sc_start(); // run forever

return 0;

}

程式說明:
在top-level裏 signal被宣告成線(wire)來使用,時脈的產生則是用sc_clock;然後把所需要的模組都實體化必加以連結起來。

都做完後,必需呼叫sc_start()來啟動模擬,模擬會一直持續執行,一直到有人呼叫了sc_stop()後結束模擬。

底下列出輸出結果的範例:

       Time A B F
0 s 0 0 1
10 ns 0 0 0
20 ns 0 1 1
30 ns 1 0 1
40 ns 1 1 0

如果你有注意看輸出結果,你應該會發生很詭異的事情,在第一行time 0s時,當時的A,B輸入為0,而F的輸出結為為1,這不應該是EXOR閘的正常輸出結果。
但在10ns之後,所有的結果都是對的了。在time 0s時到底發生了什麼事情了呢?

模擬(Simulation)
在SystemC library裏包含了一個模擬核心(simulation kernel)。它決定了那個行程(software threads)要被執行。
在time 0s時,所有的SC_METHOD及SC_THREAD將會隨機的被叫起來執行,一直到它們全都進入休眠(suppend)結束一個cycle。
SC_CTHREAD則是會在時脈一發生時就被執行。

上面的情況是由於組合電路的情況造成的

  1. sc_clock在時間time 0s會發生的上升緣(rising edge ),因此所有的monitor及stimulus行程在此時都會執行(在這種情況下,並不知道誰會先被執行)

  2. 在C++裏的變數並不見得都會被付與初使值(除非它們被宣告為靜態變數static),因此才會造成F在time 0s時會出現1的情況

  3. do_nand2 SC_METHOD在time 0s執行,而排程器也會去對F做更新,但是F是一個signal,它的內容並不能馬上被更新,因此當monitor在處理時,F的內容還會為1
在這個範例裏,改善的方式,可以去修改sc_clock讓它產生在第1個clock產生後延遲,如下:

sc_clock TestClk("TestClock", 10, SC_NS,0.5, 1, SC_NS);

加入的1, SC_NS兩個參數,定義了在第一個時脈發生後延遲1 ns,這會讓F的內容得以更新。
底下列出修改過後的執行結果:

       Time A B F
1 ns 0 0 0
11 ns 0 0 0
21 ns 0 1 1
31 ns 1 0 1
41 ns 1 1 0

現在你可以看到F的輸出結果都是正確的了。

結論
結束這篇快速瀏覽modules及process,你已經看到了SystemC模擬核心及內建的sc_signal通道的重要性。
你也看到了一些基本實體化低階模組的範例,它包含了一個top-level模組,以及sc_main是如何被使用的。

資料來源:
http://www.doulos.com/knowhow/systemc/tutorial/modules_and_processes/

SystemC Modules and Processes(1)

這個章節包含了一個完整的基本設計用以展示如何在SystemC裏使用modules及processes.
為了淺顯易懂,它是屬於非常低階的設計,這種設計方式並不是一個你在做系統層級設計時所希望的。
主要的展示重點:
  1. 建立階層架構

  2. 使用內建的sc_signal通道

  3. (特殊)ports

  4. Processes(SC_METHOD, SC_THREAD, SC_CTHREAD)

  5. 一個簡單的測試程式
SystemC的背景
為什麼要看Modules及Processes呢?這是因為System的主要目的是想要解決軟體及硬體的同時設計,並且希望能對大型的系統建立模型。
Process是程式裏的最小單位,系統裏的每個Process皆是平行運作的。機乎所有的高階系統層級設計(high-level system level design:SLD)工具都是以網絡行程(network of processes)的基礎模型(underlying model)來開發設計的。
SystemC提供了Processes(行程)來支援建構網絡裏的每個獨立(同時/平行)的程式碼區塊(pieces of code)。

SLD常會遇到大型的設計需求。為了滿足這個目的,它通常都是使用階層式(Hierarchy)的架構來設計。階層式架構在SystemC裏會以module來實現它,一個類別(class)和其它的modules的連接方式必需使用ports。
Modules允許被分割進行設計開發。Modules可能包含其它的processes以及實體化(instances)其它的modules。

一個設計範例
這個範例是以4個NAND閘組成1個EXOR閘。再次說明,這個範例並不是一個典型設計的風格,但是它可以很容易的讓大家了解SystemC。
範例電路圖如下:

第一步當然就是去建構NAND閘的模型囉。NAND閘是一種組合電路,它的輸出完全取決於輸入端,它完全沒有記憶功能,也不需要時脈。
所以這個模型(model)可以使用SystemC裏最簡單的process SC_METHOD 來設計。

SC_METHOD是一個簡單的C++函式,因此SystemC class library將它拿來展現processes的行為。
比較特別的:
SystemC class library包含了一個模擬核心(simulation kernel) - 處理每個模型的時間推移,以及當有輸入改變時呼叫函式去處理相應的輸出。
SC_METHOD需要定義function給它及觸發function的輸入值條件。(The function must be declared as an SC_METHOD and made sensitive to its inputs.)

底下是NAND閘的範例程式(nand.h)
#include "systemc.h"

SC_MODULE(nand2) // declare nand2 sc_module

{
sc_in<bool> A, B; // input signal ports
sc_out<bool> F; // output signal ports

void do_nand2() // a C++ function
{
F.write( !(A.read() && B.read()) );
}

SC_CTOR(nand2) // constructor for nand2
{
SC_METHOD(do_nand2); // register do_nand2 with kernel
sensitive << A << B; // sensitivity list
}
};
程式說明:

階層架構在SystemC裏是使用class sc_module來實現。sc_module 可能會被直接拿來使用,也可能會被使用巨集SC_MODULE指令所隱藏起來。
上面的範例裏就是使用SC_MODULE來建立一個sc_module名字叫nand2的類別物件。

接下來宣告輸入及輸出埠。一般來說,一個埠的宣告都是使用類別sc_port。
再進一步的例子,input ports還會需要使用sc_signal來宣告型態。如:
sc_port<sc_signal_in_if<bool>,1> A,B;
所以就如同你看到的,會有很多很多的形態耶。所以為了方便,也可以使用特別的ports.
sc_in就是一個特殊的埠用以快速的使用sc_signal。

ports可以是C++或SystemC的任何一種形態,範例裏是使用C++內建的bool形態。

再接下來介紹,input及output ports都包含了read()/write()的方法(methods),使用它們來讀/寫埠的資料。
A和B都是被拿來讀資料的,do_nand2()是拿來計算的函式,而計算結果將透過write()寫入到F裏。

注意一下你也可以不使用read()/write()來讀/寫資料,用=來進行操做,如:
F = !(A && B);
但是使用read()/write()是比較好的作法,它比較容易讓C++的編譯程式進行分析。

在看完do_nand2()後,接著是建構式sc_module,它實體化了nand2這個物件。SystemC提供了簡單的方法來完成這件事,那就是SC_CTOR。
這個建構式處理的事情如下:
  1. 建立階層(不過在這個範例中沒有)

  2. 在simulation kernel裏註冊我們這個新的process

  3. 宣告這個的觸發條件清單列表


它也可能在建構式裏做一些初使化的的行為 - 比如說:類別資料成員就可以在建構式裏進行初使化。

所以在上面的範例程式裏,建構式宣告了do_nand2是一個SC_METHOD,並且告訴kernel當port A/B值發生改變時去執行do_nand2()。

資料來源:
http://www.doulos.com/knowhow/systemc/tutorial/modules_and_processes/

2008年12月26日 星期五

神奇的隱形

隱形斗篷 - 日本大學的實驗





傳說中的「隱形斗篷」將於IEDM亮相

如果你是「星艦迷航記(Star Trek;或譯銀河飛龍)」系列影集的忠實粉絲就應該記得,總是扮演反派角色的羅慕倫人(Romulans),最會用隱形裝置來藏太空船,令人防不勝防。

現在這種隱形裝置有可能成為現實,在近日於美國舉行的國際電子元件會議(IEDM)上,美國波士頓大學的研究人員將發表一種軟性、可實現隱形斗篷(防護罩)概念的「超常介質(metamaterial;或譯超物質)」技術。

據了解,波士頓大學將介紹一種安裝在聚亞醯胺( polyimide)基板上的軟性圓柱狀超常介質材料,以兆赫(terahertz,THz)速度運作。超常介質是一種「次波長(sub-wavelength)」複合材料,可用來合成金屬或是支援電介質(dielectrics)。

上述的超常介質結構是由200mm厚的金與10mm鈦黏著層,在聚亞醯胺基板上所組成;該基板是用液態聚亞醯胺透過旋轉塗佈(spin-coated)法,在塗佈了400nm厚的噴濺氮化矽薄膜的2吋矽晶圓上所形成。

該基板包含了純電子共振器與標準開環共振器(Split Ring Resonator);軟性共振材料依次安裝在聚亞醯胺基板上,以製作大量的多層、非平面電磁複合物。這種複合材料就可用來做為於0.5 THz頻率下能隱形的遮罩。

(參考原文:Group demos metamaterials for invisible cloak,by Mark LaPedus)

資料來源:
http://www.eettaiwan.com/ART_8800556757_480102_NT_a5d84932.HTM

解讀「終極SoC」密碼──MEMS完全手冊

從實驗室中的基礎研究,到近來開始滲入消費性電子產品,微機電系統(MEMS)的廣泛應用已成為產業發展的一個重要趨勢,值得密切關注。為了讓讀者們更容易掌握MEMS領域發展趨勢,《電子工程專輯》收集了近來針對MEMS的相關報導與技術文章,製作了一份『解讀「終極SoC」密碼──MEMS完全手冊』電子書,歡迎有興趣的讀者踴躍下載或傳閱。
E Book Mems
View SlideShare document or Upload your own.

資料來源:
http://www.eettaiwan.com/ART_8800463781_480502_NT_973e8fb3.HTM

Soc ESL簡單介紹

這篇文章是希望能幫助大家了解現代嵌入式系統和SoC設計工具的概念及其使用方法,以期在SoC設計中實現電子系統級模擬及設計空間探索,為嵌入式的軟體和硬體作出最新最複雜的整合性現代設計。
內容包括
(1)什麼是ESL
(2)ESL和嵌入式系統SoC設計
(3)抽象模型建立與系統設計方法
(4)嵌入式系統軟硬體電子化設計工具鏈和流程
(5)工具鏈介紹以AndeSight為例
(6)ESL tool--Andes AndESLive 介紹
(7)展望
作者: 謝達人
20080701 Oe Andes Ta 02
View SlideShare document or Upload your own.


資料來源:
http://www.eettaiwan.com/ART_8800531764_676964_TA_efafd008.HTM

2008年12月25日 星期四

EFI - SEC(Security)階段

EFI - SEC(Security)階段

SEC階段是EFI的第一個執行階段,當電腦Power-On時就會進入此階段,原則上EFI只有這個部份會使用組合語言搭配C語言撰寫,此階段的重點目的是把CPU的SP(Stack Pointer)指到CPU內部的Cache中。

SEC階段主要功能
  1. 掌控平台的restart事件

  2. Real Mode to Protected Mode

  3. 使用CPU Cache當做記憶體(Creates a temporary memory store)

  4. 因為在此階段時,北橋裏的Memory Controller還沒有初使化,尚無法使用系統記憶體,但C語言執行時需要Stack Area,因此暫時先拿Cache來做堆疊區。
  5. 進入下個PEI階段(Passes handoff information to the PEI)


更詳細的參考資料可以看Intel® Platform Innovation Framework for EFI Architecture Specification - Draft for Review

2008年12月4日 星期四

Moblin Project:Fast Boot

http://moblin.org/projects/fast-boot

這是MID的官網,介紹了MID如何讓系統在5秒內開完機及Demo唷!

5秒開完的簡報檔
http://moblin.org/sites/all/files/u123/plumbers_fastboot.pdf

Demo影片
http://www.youtube.com/watch?v=s7NxCM8ryF8

真是超快的!

2008年11月26日 星期三

Ubuntu安裝Vim Plugin ctags及taglist

安裝ctags及taglist
  1. 首先先安裝ctags

  2. apt-get install exuberant-ctags

  3. 然後到Taglist網站去下載zip檔http://vim-taglist.sourceforge.net/

  4. (1)下載後解壓縮該檔案
    # unzip taglist_45.zip
    Archive: taglist_45.zip
    inflating: plugin/taglist.vim
    inflating: doc/taglist.txt
    (2)將plugin及doc複製到家目錄的".vim"這個目錄裏,如果不存在就建一個新的".vim"
在vim裏設定功能鍵F9 - 用它來開啟/關閉taglist功能
在/etc/vim/vimrc裏加入下面1行
map <f9> :Tlist<CR>
最後一個步驟,到你的原始程式目錄裏輸入

ctags -R *.c *.h

開啟vim xxx.c按F9
你就會看見如下圖的結果

畫面分成左右兩邊,切換方式用Ctrl-W-W

左邊畫面TagList只要按Enter就會自動跳到相對應的函式或定義

右邊的畫面:
Ctrl+] : 跳到游標所指function的implement 位置 ,注意游標要在function的第一個字。
Ctrl+t : 返回到剛剛的位置。

cs find
    s: 查找C语言符号,即查找函数名、宏、枚举值等出现的地方
    g: 查找函数、宏、枚举等定义的位置,类似ctags所提供的功能
    d: 查找本函数调用的函数
    c: 查找调用本函数的函数
    t: 查找指定的字符串
    e: 查找egrep模式,相当于egrep功能,但查找速度快多了
    f: 查找并打开文件,类似vimfind功能
    i: 查找包含本文件的文件
範例: cs find t memset <-- memset="" pre="">

2008年11月25日 星期二

Linux啓動分析(2)— bootsect.S、setup.S、head.S分析[轉貼]

bootsect.S,系統引導程序,一般不超過512字節。
在PC系統結構中,綫性地址0xA0000以上,即640K以上用于圖形接口卡和BIOS自身,640K以下為系統的基本内存。如果配置更多的内存,則0x100000,即1MB處開始稱為高内存。當BIOS引導一個系統時,總是把引導扇區讀入到基本内存地址為0x7c00的地方,然後跳轉到此執行引導扇區的代碼。這段代碼將自身搬運到0x90000處,並跳轉到那繼續執行,然後通過BIOS提供的讀磁盤調用“int 0x13”從磁盤上讀入setup和内核映像。其中setup的映像讀入到0x90200處,然後跳轉到setup的代碼中。

從0x90000到0xA0000一共64K,bootsect僅占512字節,所以setup大小理論上可到63.5KB。
在Linux2.4版本以前,在最前面的512字節裏保護了一個mini “boot loader”,只要拷貝啓動代碼運行就可從軟碟啓動;但在2.6版本中不再保護這様的”boot loader”,所以必須在第一個磁盤分區上存儲一個合適的boot loader才能從軟碟啓動,軟碟、硬碟和光驅啓動都是一様的過程。
setup進行映像的解壓縮,從BIOS收集一些數據,在控制台顯示一些信息。

基本内存中開頭一部分空間是保留給BIOS自己用的,另一方面對于Linux内核的引導也需要保留一些運行空間,一共保存了64K。基本内存中用于内核映像的就是8*64K=512K,其中頂端留4K用于引導命令行及從BIOS獲取需要傳遞給内核的數據。内核映像一般都經過壓縮,壓縮後的映像和引導扇區及輔助引導程序的映像拼接在一起,成為内核的引導映像。大小不超過508K的映像稱為小映像zImage,早期版本放在0x10000位置處,否則稱為大内核bzImage,放在0x100000位置處。

CPU在bootsect時處于16位實地址模式,然後在setup的執行過程中轉入32位保護模式。
Setup從BIOS中讀取系統數據(内存大小、顯卡模式、磁盤等參數),將數據保存在0x90000-0x901FF,覆蓋了bootsect的内容。設置32位運行方式:加載中斷描述表寄存器IDTR、全局描述表寄存器GDTR;臨時設置IDT表和GDT表,並在GDT表中設置内核代碼段和數據段的描述符,在Head.S中會根據内核的需要重新設置這些描述符表;開啓A20地址綫;重新設置兩個中斷控制器8259A,將硬件中斷號重新設置為0x20和0x2f;最後設置CPU的控制寄存器CR0(機器狀態字)的保護模式比特(PE)位,從而進入32位保護模式運行;然後跳轉到head.S中的startup_32執行。

對于小内核映像放在0x10000處,Setup會把system從0x10000移到0x0000開始處。對于大内核映像,vmlinux中普通内核代碼被編譯成以PAGE_OFFSET+1MB為起始地址,在Head.S中初始化代碼把虚擬地址减去PAGE_OFFSET就能得到以1MB為起始位置的物理地址,這也正是内核映像在物理内存中的存放位置。
Head.S中的startup_32主要用于開啓頁面單元。初始化工作在編譯過程中開始進行,它先定義一個稱為swapper_pg_dir的數組,使用鏈接器指示在地址0x00101000。然後分彆為兩個頁面pg0和pg1創建頁表項。第一組指向pg0和pg1的指針放在能覆蓋1~9MB内存的位置,第二組指針放在PAGE_OFFSET+1MB的位置。一旦開始頁機制,在上述頁表和頁表項指針建立後可以保證,在内核映像中不論是采用物理地址還是虚擬地址,都可以進行正確的頁面映射。内核其他部分的頁表初始化在paging_init()中完成。映射建立後,通過設置cr0寄存器中的某位開啓頁面映射,然後通過一個跳轉指令保證指令指針的正確性。

(1).Bootsect啓動過程:
假設用LILO啓動,啓動時用户可以選擇啓動哪個操作系統。LILO將boot loader分為兩部分,一部分放到啓動分區的第一個扇區;
  1. BIOS將MBR或啓動分區的第一個扇區的啓動部分加載到地址0x00007c00處;

  2. 該程序將自身移到0x00096a00,建立實模式棧(從0x00098000到0x000969ff),將LILO的第二部分加載到0x00096c00處,然後跳轉到此執行;

  3. 然後第二部分程序從磁盤讀取一個可啓動的操作系統列表讓用户選擇,最後用户選擇每個OS後,boot loader可以拷貝不啓動分區或者之間拷貝内核映像到RAM中去;

  4. 加載Linux内核映像時,LILO boot loader首先調用BIOS例程顯示”Loading …”信息;

  5. 調用BIOS例程加載内核映像的初始化部分到RAM上,内核映像的前512字節放在0x00090000位置,setup()函數代碼放在0x00090200位置;

  6. 接着調用BIOS例程裝載内核映像的其餘部分,映像可能放在低地址0x00010000(使用make zImage編譯的小内核映像)或者高地址0x00100000(使用make bzImage編譯的大内核映像)。

  7. 然後跳至剛剛setup部分。


(2).Setup.S分析
setup()匯編函數被連接器放在内核映像文件中的0x200偏移處。Setup函數必須初始化計算機中的硬件設備並為内核程序的執行建立環境。
  1. 在ACPI兼容的系統中,調用BIOS例程建立描述系統物理内存布局的表。在早期系統中,它調用BIOS例程返回系統可以的RAM大小;

  2. 設置鍵盤的重復延遲和速率;

  3. 初始化顯卡;

  4. 檢測IBM MCA總綫、PS/2鼠標設備、APM BIOS支持等;

  5. 如果BIOS支持Enhanced Disk Drive Services (EDD),將調用正確的BIOS例程建立描述系統可用硬碟的表;

  6. 如果内核加載在低RAM地址0x00010000,則把它移動到0x00001000處;如果映像加載在高内存1M位置,則不動;

  7. 啓動位於8042鍵盤控制器的A20 pin。

  8. 建立一個中斷描述表IDT和全局描述表GDT表;

  9. 如果有的話,重啓FPU單元;

  10. 對可編程中斷控制器進行重新編程,屏蔽所以中斷,級連PIC的IRQ2不需要;

  11. 設置CR0狀態寄存器的PE位使CPU從實模式切换到保護模式,PG位清0,禁止分頁功能;

  12. 跳轉到startup_32()匯編函數, jmpi 0x100000, __BOOT_CS,終于進入内核Head.S;


(3).Head.S分析
有兩個不同的startup_32()函數,一個在arch/i386/boot/compressed/head.S文件中,setup結束後,該函數被放在0x00001000或者0x00100000位置,該函數主要操作:
  1. 首先初始化段寄存器和臨時堆棧;

  2. 清除eflags寄存器的所有位;

  3. 將_edata和_end區間的所有内核未初始化區填充0;

  4. 調用decompress_kernel( )函數解壓内核映像。首先顯示"Uncompressing Linux..."信息,解壓完成後顯示 "OK, booting the kernel."。内核解壓後,如果時低地址載入,則放在0x00100000位置;否則解壓後的映像先放在壓縮映像後的臨時緩存裏,最後解壓後的映像被放置到物理位置0x00100000處;

  5. 跳轉到0x00100000物理内存處執行;


解壓後的映像開始于arch/i386/kernel/head.S 文件中的startup_32()函數,因為通過物理地址的跳轉執行該函數的,所以相同的函數名並没有什麽問題。該函數未Linux第一個進程建立執行環境,操作如下:
  1. 初始化ds,es,fs,gs段寄存器的最終值;

  2. 用0填充内核bss段;

  3. 初始化swapper_pg_dir數組和pg0包含的臨時内核頁表:

    • 將swapper_pg_dir(0x1000)和pg0(0x2000)清空,swapper_pg_dir作為整個系統的頁目録;

    • 將pg0作為第一個頁表,將其地址賦到swapper_pg_dir的第一個32位字中。

    • 同時將該頁表項也賦給swapper_pg_dir的第3072個入口,表示虚擬地址0xc0000000也指向pg0。

    • 將pg0這個頁表填滿指向内存前4M。

    • 在cr3寄存器中存放PGD的地址,並設置cr0寄存器中的PG位,啓用分頁支持。

  4. 建立進程0idle進程的内核模式的堆棧;

  5. 再次清除eflags寄存器的所有位;

  6. 調用setup_idt()用非空的中斷處理函數填充IDT表;

  7. 將從BIOS獲取的系統參數傳遞到操作系統的第一個頁面幀;

  8. 識別處理器的模式;

  9. 將GDT和IDT表的地址加載到gdtr和idtr寄存器中;

  10. 跳轉到start_kernel函數,這個函數是第一個C編制的函數,内核又有了一個新的開始。


(4).start_kernel()分析:
  1. 調度器初始化,調用sched_init();

  2. 調用build_all_zonelists函數初始化内存區;

  3. 調用page_alloc_init()和mem_init()初始化夥伴系統分配器;

  4. 調用trap_init()和init_IRQ()對中斷控制表IDT進行最後的初始化;

  5. 調用softirq_init() 初始化TASKLET_SOFTIRQ和HI_SOFTIRQ;

  6. Time_init()對系統日期和時間進行初始化;

  7. 調用kmem_cache_init()初始化slab分配器;

  8. 調用calibrate_delay()計算CPU時鐘頻率;

通過調用kernel_thread()啓動進程1init進程的内核綫程,然後該綫程再創建其他的内核綫程執行/sbin/init程序。

資料來源:
http://blog.csdn.net/cxylaf/archive/2007/05/26/1626513.aspx

Linux啓動分析(1)— 總體過程[轉貼]

系統引導過程總體介紹
啓動流程:
系統引導過程主要由以下幾個步驟組成(以硬碟啓動為例)
  1. 開機;

  2. BIOS加電自檢(POST——Power On Self Test),包括檢查RAM,keyboard,顯示器,軟硬磁盤等等。Intel系列的CPU首先進入的是實模式,並開始執行位於地址0xFFFF0處的代碼,也就是ROM-BIOS起始位置的代碼;

  3. 搜索啓動的操作系統,根據BIOS設置,可能會依次訪問每個軟碟的第一個扇區、硬碟、CD-ROW等;一旦找到有效的啓動設備,將第一個扇區(0頭0道1扇區,也就是Boot Sector)的内容讀入内存地址0x7c00處;

  4. 檢查(WORD)0000:7dfe是否等于0xaa55.若不等于則轉去嘗試其他介質;如果没有其他啓動介質,則顯示 “No ROM BASIC” ,然後當機;

  5. 跳轉到0000:7c00處執行MBR中的程序bootsect.S;

  6. MBR先將自己復制到0x90000處,然後將緊接其後的setup部分(第二扇區)拷貝到0x90200,將真正的内核代碼拷貝到0x100000。以上這些拷貝動作都是以bootsect.S、setup.S以及vmlinux在磁盤上連續存放為前提的;

  7. bootsect.S完成加載動作後,就直接跳轉到0x90200,這裏正是setup.S的程序入口。 setup.S的主要功能就是將系統參數(包括内存、磁盤等,由BIOS返回)拷貝到 0x90000-0x901FF内存中,這個地方正是bootsect.S存放的地方,這時它將被系統參數覆蓋。以後這些參數將由保護模式下的代碼來讀取。

  8. setup.S還將video.S中的代碼包含進來,檢測和設置顯示器和顯示模式。最後,setup.S將系統轉换到保護模式,並跳轉到0x100000(對于bzImage格式的大内核是 0x100000,對于zImage格式的是0x1000)的内核引導代碼,Bootloader過程結束;

  9. Bootloader跳轉到0x100000, 此處為"arch/I386/init/head.S"中的startup_32, startup_32的代碼只需要設置一下全局變量,然後就跳轉到start_kernel去了;start_kernel()是"init/main.c"中的asmlinkage函數,至此,啓動過程轉入體系結構無關的通用C代碼中;

  10. start_kernel()中設置與體系結構相關的環境、頁表結構初始化、Trap/IRQ初始化、核心進程調度器初始化、時間/定時器初始化、控制台初始化、核心Cache初始化、内存初始化、内部及通用等各種Cache初始化、信號量初始化、其他部分初始化(Init()及smp_init());

  11. 啓動Init()過程,創建第一個進程;Init()中,取得 run-level 信息, 執行 /etc/rc.d/rc.sysinit 脚本, 激活核心的外挂式模塊 (/etc/modules.conf), 然後init 執行 run-level 的各個脚本, 接着執行 /etc/rc.d/rc.local脚本, 最後執行 /bin/login 程序, 登入之後開始以 Shell 控管主機;

  12. 啓動完成。


資料來源:
http://blog.csdn.net/cxylaf/archive/2007/05/26/1626511.aspx

2008年11月18日 星期二

SystemC 時間

SystemC支援的時間單位包括SC_FS、SC_PS、SC_NS、SC_US、SC_MS及SC_SEC,而用在時間值的資料型別都是未帶號(unsigned)64位元的整數。
enum sc_time_unit
{
SC_FS = 0, // femtosecond
SC_PS, // picosecond
SC_NS, // nanosecond
SC_US, // microsecond
SC_MS, // millisecond
SC_SEC // second
};

語法如下:
sc_time var_name(time_value, time_unit);

如果想要產生一個clock信號,可以使用下面的寫法:
sc_time t(20, SC_NS);
sc_clock clk("clk", t);


更詳細的範例可參考:
http://www.eecs.berkeley.edu/~hiren/docs/sc_intro.pdf

2008年11月13日 星期四

Verilog and SystemC 比較

(1)Module的寫法
Verilog
module module_name(input/output declaration)
variable declaration
computation block
endmodule

SystemC
SC_MODULE(module_name){
input/output declaration
internal variable
constructor (computation block)
};

(2)input/output定義
Verilog
Input:input var1, ...;
Output:output var2, ...;
Type

SystemC
Input:sc_in var1, ...;
Output:sc_out var2, ...;
Type
  • C++ primitive type: int, float, char, ...
  • hardware type: sc_int, sc_uint, ...
  • user defined type

(3)Computation Block
Verilog
Event trigger: always@(a or b or c)
Edge trigger: always@(posedge clk)

SystemC
SC_CTOR(module_name){
SC_METHOD(function name);
sensitive << a << b << c;
...
}

參考資料: http://twins.ee.nctu.edu.tw/courses/soc_sys_overview_04fall/lab/systemc_chapter1.ppt

全加器範例程式

這是一個SystemC的全加器模型程式
共有main.cpp,module.cpp module.h三支程式
裏面有3個物件(1)full adder[全加器](2)pattern generator[訊號產生器](3)monitor[監控程式]
main.cpp
#include <systemc.h>
#include <iostream>
#include "module.h"

int sc_main(int argc, char** argv){
        sc_signal<int> a, b, carry, co, sum;

        //module ptr
        full_adder adder("adder");
        adder.carry(carry);
        adder.a(a);
        adder.b(b);
        adder.sum(sum);
        adder.co(co);

        pattern_gen pg("pattern_gen");
        pg.d_a(a);
        pg.d_b(b);
        pg.d_carry(carry);

        monitor mon("monitor waveform");
        mon.m_a(a);
        mon.m_b(b);
        mon.m_carry(carry);
        mon.m_sum(sum);
        mon.m_cout(co);


        sc_start(50, SC_NS);

        return 0;
}

module.h
#include <systemc.h>

SC_MODULE(full_adder){
        sc_in<int> carry;
        sc_in<int> a;
        sc_in<int> b;

        sc_out<int> sum;
        sc_out<int> co;


        void proc_full_adder();

        SC_CTOR(full_adder){
                SC_METHOD(proc_full_adder);
                sensitive << carry << a << b;
        }
};

SC_MODULE(pattern_gen){
        sc_out<int> d_a;
        sc_out<int> d_b;
        sc_out<int> d_carry;

        void proc_pattern_gen();

        SC_CTOR(pattern_gen){
                SC_THREAD(proc_pattern_gen);
        }
};

SC_MODULE(monitor){
        sc_in<int> m_a, m_b, m_carry, m_sum, m_cout;

        void proc_monitor();

        SC_CTOR(monitor){
                SC_THREAD(proc_monitor);
                //sensitive << m_a << m_b << m_carry << m_sum << m_cout;
                sensitive << m_a << m_b << m_carry;
        }
};

module.cpp
#include <systemc.h>
#include <iostream>
#include "module.h"
//using namespace std;

void full_adder::proc_full_adder(){
        sum = a ^ b ^ carry;
        co = (a & b) | (b & carry) | (carry & a);
}

void pattern_gen::proc_pattern_gen(){
        sc_uint<3> pattern;
        pattern = 0;

        while (1){
                d_a = pattern[0];
                d_b = pattern[1];
                d_carry = pattern[2];

                wait(5, SC_NS);
                pattern++;
        }
}

void monitor::proc_monitor(){
        while (1){
                cout << "At time " << sc_time_stamp() << "::";
                cout << "(a, b, carry): ";
                cout << m_a << m_b << m_cout;
                cout << " (sum, carry_out): " << m_sum << m_cout << endl;
                wait();
        }
}

Makefile
LIB_DIR=-L/usr/systemc/lib-linux
CPPFLAGS=-I/usr/systemc/include

LIB=-lsystemc

APP=main
OBJS = main.o module.o

$(APP):$(OBJS)
        g++ -o $@ $^ $(LIB_DIR) $(LIB)
.c.o:
        g++ $(CPPFLAGS) -c $@ $<

clean:
        rm -rf $(APP) *.o


執行結果:
At time 0 s::(a, b, carry): 000 (sum, carry_out): 00
At time 5 ns::(a, b, carry): 100 (sum, carry_out): 00
At time 10 ns::(a, b, carry): 010 (sum, carry_out): 10
At time 15 ns::(a, b, carry): 110 (sum, carry_out): 10
At time 20 ns::(a, b, carry): 001 (sum, carry_out): 01
At time 25 ns::(a, b, carry): 100 (sum, carry_out): 10
At time 30 ns::(a, b, carry): 011 (sum, carry_out): 01
At time 35 ns::(a, b, carry): 111 (sum, carry_out): 01
At time 40 ns::(a, b, carry): 001 (sum, carry_out): 11
At time 45 ns::(a, b, carry): 100 (sum, carry_out): 00
資料來源:
http://twins.ee.nctu.edu.tw/courses/soc_sys_overview_04fall/lab.html

2008年11月8日 星期六

EFI UGA & Simple Pointer[轉貼]

年前學習了有關UGA & Simple Pointer的知識,今天拿出來,總結一下。方便自己以後的理解和查閲。
UGA
UGA是 Universal Graphics Adapter (通用圖形適配器)的縮寫。在本質上講,UGA 是一個EFI driver。它在OS導入前和OS運行時都能被使用。

1,Universal Graphics Adapter Protocols主要描述了有關在EFI環境下的圖象顯示。這個部分包含了 UGA Draw Protocols 和 UGA I/O protocol 。前者描述了如何在 pre-OS space 繪入圖象,在圖象屏幕上顯示出來;後者描述了如何訪問圖象屏幕,以及支持視頻控制器的子設備,例如:圖象顯示設備。同時,後者的目標是在 OS 當前的環境下,實現初步的使用。

2,UGA ROM 是一個軟件的概念,它的目標是來支持可預知圖形硬件,并不要求VGA硬件。(不是很清楚)

3,UGA Draw Protocol 支持三個成員函數,來支持 pre-OS space 有限的圖形需求。這些成員函數允許調用者draw到虚擬的 frame buffer 裏面,來獲得當前的視頻模式,以及設置視頻模式。這些簡單的原始函數已經能充分的滿足pre-OS firmware code的總的需要。

4,在 EFI_UGA_DRAW_PROTOCOL 基本圖形操作是 Blt(Block Transfer)。Blt操作允許數據讀出和寫入視頻適配器的視頻存儲器裏。frame buffer 視頻顯示是由一組像素。每個像素在視頻顯示上的位置由 X 和 Y 坐標描述。X 代表了一個掃描行。一個掃描行是指顯示的水平的像素大小。 software Blt buffer 也是由一組像素來組成的。在buffer 裏的第一個元素是Pixel (0, 0) 。Blt buffer 可以看成是一系列的掃描行。一個像素在視頻顯示上的位置和 Blt buffer 裏相對應的轉化:Blt buffer array index = Y * Width + X 。Blt buffer 裏描述像素是有32位,字節0到字節2分彆代表了在像素中紅,緑,藍三色各含的成分,字節3是保留位,始終為0。

5,UGA I/O Protocol 支持I/O 請求模式,目的在于給 OS high performance driver 提供服務。這些I/O請求是經由 EFI_UGA_IO_PROTOCOL DispatchService() 成員函數來訪問的。I/O請求服務包含的能力由 EFI_UGA_DRAW_PROTOCOL 來支持。

6,EFI_UGA_DRAW_PROTOCOL 的三個成員函數:GetMode()、SetMode()、Blt();
EFI_UGA_IO_PROTOCOL 的成員函數:CreateDevice()、DeleteDevice()、DispatchService();

Simple Pointer
1,指定一個簡單的方法來進入指針設備。通常説來,主要是指鼠標。EFI_SIMPLE_POINTER_PROTOCOL 允許返回指針設備的有關信息。這包含了按鍵的狀態和最新訪問時鼠標的運動狀態。

2,每個EFI_SIMPLE_POINTER_PROTOCOL都必須安裝Handle 來給EFI Drivers和EFI App提供一些服務讓它們來利用。同時,EFI_DEVICE_PATH 也必須安裝同様的Handle。

相關的概念理解:
1,EBC:
對于不同的處理器和平台,Option ROMs要求有不同的可執行的映像。EFI定義了EFI 字節碼編譯器(EBC)虚擬機來處理這些不同。這個虚擬器的注釋器是firmware的一部分。C語言能够被編譯成EBC,然後和相關的驅動連接,在注釋器上運行。
C語言編譯成 EBC ,同時創建 EBC 映像,它可以被系統在 EFI1.10、UEFI2.0,或更後的Spec下執行。這些系統包含 EBC 注釋器,EBC 注釋器加載和解釋 EBC image,允許 image 能够在多種平台和機制上執行,包括那些基于英特爾? 安騰?處理器, 32位英特爾? 架構處理器,以及64位英特爾體系架構處理器。

2,OPROM:
(Option ROM) Firmware 在適配器卡上控制可啓動的外圍設備。系統 BIOS 會詢查 option ROMs,來確定有哪些設備可以被啓動導入。
(Option ROM) Firmware on adapter cards that control bootable peripherals. The system BIOS interrogates the option ROMs to determine which devices can be booted.

Option ROM 有固件組成,由系統 BIOS 來調用。舉例來説:適配器卡控制導入設備,設備可能包含固件,一旦 Option ROM 被加載,固件用來將設備連接到系統。
An Option ROM typically consists of firmware that is called by the system BIOS. For example, an adapter card that controls a boot device might contain firmware that is used to connect the device to the system once the Option ROM is loaded.

資料來源:
http://blog.sina.com.cn/s/blog_4b265789010008fy.html
EFI Image綜述

EFI Driver Template Code

#include "Tiano.h"
#include "EfiDriverLib.h"
#include "DrvSampleSetup.h"
#include "DrvSample.h"

//
// Local function declarations
//

EFI_STATUS
DrvSampleEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);

EFI_DRIVER_ENTRY_POINT (DrvSampleEntry)

EFI_STATUS
DrvSampleEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
/*++

Routine Description:

DrvSample driver entry point function.

Arguments:

ImageHandle - image handle for this driver image
SystemTable - pointer to the EFI System Table

--*/

{
EFI_STATUS Status;
EFI_HANDLE Handle;

Status = EFI_SUCCESS;
Handle = NULL;
EfiInitializeDriverLib (ImageHandle, SystemTable);
//
// TODO: Add implementation code
//
//
// Initialize our setup/forms data
//

Status = SetupInit (ImageHandle, SystemTable, &Handle);
if (EFI_ERROR(Status)) {
//
// TODO: Handle error
//

}
//
// Initialize and produce our driver binding protocol
//

Status = DriverBindingProtocolInit (ImageHandle, SystemTable, &Handle);
if (EFI_ERROR(Status)) {
//
// TODO: Handle error
//

}
//
// Initialize and produce our driver diagnostics protocol
//

Status = DriverDiagnosticsProtocolInit (ImageHandle, SystemTable, &Handle);
if (EFI_ERROR(Status)) {
//
// TODO: Handle error
//

}
//
// Initialize and produce our component name protocol
//

Status = ComponentNameProtocolInit (ImageHandle, SystemTable, &Handle);
if (EFI_ERROR(Status)) {
//
// TODO: Handle error
//

}
//
// Initialize and produce our driver configuration protocol
//

Status = DriverConfigurationProtocolInit (ImageHandle, SystemTable, &Handle);
if (EFI_ERROR(Status)) {
//
// TODO: Handle error
//

}
return Status;
}


資料來源:
http://blog.sina.com.cn/s/blog_4b265789010006gu.html
EFI Programming on Mac OS X

2008年11月7日 星期五

Simple Guide for Porting Android Kernel[轉貼]

作者:劉旭暉colorant@163.com轉載請註明出處
http://blog.csdn.net/colorant/

移植Android的kernel到實際的硬件平台上,很多人很早就做過這件事了,不過相關的文檔和經驗總結不多,我就寫一個吧,也為了自己記錄一下大致的流程,以後好繼續。

1. Android內核Porting相關背景知識

1.1 運行平台
Google的Android平台到今天為止(2008-2-27),應用部分還是以二進制的Binary的形式發布的,其编譯的目標平台是ARM926EJ-S的CPU屬於ARMV5T的版本,所以ARMV4架購的CPU平台無法使用其二進制代碼。關於這點,可以参考下面這篇文章,Benno在此做了詳細的理論分析和代碼測試:http://benno.id.au/blog/2007/11/21/android-neo1973 . 所以目前只有基於ArmV5或以上的架構的平台可以實際運行Android。

1.2 軟件環境

SDK下載:

KERNEL,模擬環境等SRC包下載: http://code.google.com/p/android/downloads/list

1.2.1 Kernel

到M5-r14 release為止,Android的Kernel是基於Linux2.6.23的內核開發的,主要添加了一個名為Goldfish的虛擬CPU以及Android所需相關特定驅動代碼。你需要一個支持EABI的內核作為你內核Porting的起點(最低版本?不知道,只要EABI OK,應該沒有本質區別,但是,Android的很多驅動依賴於2.6.23的內核API,版本越低的內核,移植修改內核相關代碼的工作量越大)

1.2.2 Toolchain

SDK中的內核使用的是4.2.1版本的GCC,基本上,你需要的是一個支持EABI的工具鏈,比如你可以使用Codesourcery的最新工具鏈: http://www.codesourcery.com/

1.2.3 其他工具

Android的Emulator是一個很好的仿真工具,其底層是基於QEMU來實現的,可以使用SDK中的adb工具登陸Emulator的控制台,和控制台交換文件等,用於獲取你所需的信息。

1.3 相關論壇資源

http://benno.id.au/blog/

http://groups.google.com/group/android-internals

http://groups.google.com/group/android-developers

2. Porting 基本思路

2.1 所需資源

2.1.1 硬件

首先,當然是需要一個可以用來向上porting的硬件開發板了,對硬件的需求除了上面說的,需要ArmV5+兼容的CPU以外,最低要求基本需要64M+的內存,64M-128M+的FLASH(取決於你加載文件系統的方式,如果可以透過網絡使用NFS-ROOT或者MMC卡等來存放文件系統的話,這個應該就無所謂了)

2.1.2 軟件

除了上述kernel和tools chain,為了方便調試,最好有靜態編譯的Busybox和Strace等工具。 也可以從Benno的blog上下載到他編譯好的版本。也可以從Benno的blog上下載到他編譯好的版本。

2.2 基本流程

下載Android內核代碼

(1).下載官方2.6.23内核
(2).製作Android和2.6.23内核的diff文件
(3).去除diff文件中和Goldfish和QEMU相關的代碼,如果你的系統已經支持YAFFS2,還可以去除這部分代碼去除diff文件中和Goldfish和QEMU相關的代碼,然後將diff文件Patch到你自己的内核上,如果需要,修改内核相關文件代碼使得patch能夠順利完成。將diff文件Patch到你自己的內核上,如果需要,修改內核相關文件代碼使得patch能夠順利完成。 (這部分大概是主要的工作量,如果你的内核版本差得比較遠的話)
(4).如果必要,修改你的内核代碼中Framebuffer的驅動,使其Virtual_yres 等於兩倍的Yres,並實際分配兩倍分辨率大小的framebuffer内存。

配置內核,確保下列內容得到配置:
CONFIG_ARM_THUMB=y
CONFIG_AEABI=y
CONFIG_BINDER=y
CONFIG_ANDROID_LOG=y
CONFIG_ANDROID_POWER=y
CONFIG_ANDROID_POWER_STAT=y

*從SDK中獲取Android的文件系統,基本上你只需要System etc sbin init這幾個目錄/文件就可以了,其它自建,其中data目錄是有內容的,但是這個目錄的內容可以由Android在啟動時動態的創建出來。
*可以使用adb工具在EMULATOR先tar包裝,再拷貝出來。M3的release也可以從benno那裡直接拿到他抓出來的文件系統
*確保你的dev目錄下有足夠系統啟動的設備節點,如console等,其它的節點Android在啟動過程中會自動創建出來。
*使用NFSROOT或者chroot等手段Android的文件系统。

啟動流程的大致外在表現分階段依次是:

Ø LCD上出現Android幾個字符
Ø LCD短時間的Blank
Ø LCD上出現一個左右滾動的紅色滾動條 (如果有問題,基本上就死在這一步了)
Ø 進入主界面
目前為止我的狀態是:鍵盤可以工作,觸摸屏有響應但是未校準,位置不對,啟動最後階段以及之後啟動新的程序,出現Vmalloc分配內存Failed問題,導致如Brower等應用程序不能完全啟動。 其它網絡等東東還沒開始看呢。

3. 一些TIPS

*Android會對文件使用memory mapped的方式進行操作,JFFS2不支持這種操作,所以要使用別的文件系統。 當然也有繞過去的辦法,自己搜一下吧。
*為了方便測試,可以修改/etc/init.rc,註釋掉runtime,dbus-daemon,Xzygote等相關內容,在init啟動以後再手工啟動這些進程:

/system/bin/app_process -Xzygote /system/bin –zygote &
/system/bin/dbus-daemon –system –nofork & /system/bin/dbus-daemon –system –nofork &
sleep 1; sleep 1;
/system/bin/runtime & /system/bin/runtime &

*Android的Init位於根目錄下,所以如果你需要直接啟動Init,可以在內核參數命令行中用init=/init來指定,或者chroot目錄/init來指定。 當然,啟動/bin/sh以後,再手動啟動/init也是可以的。
*/dev/binder /dev/alarm /dev/log/*等文件是最重要的幾個設備節點,由於這幾個設備節點號的主次設備號是動態分配的,所以,最好確認你的文件系統中的這幾個設備節點的主次設備號是否正確。 如果不知道如何確認,直接删除掉再重啟動。
*如果flash速度太慢或者nfs網絡連接太差,可以將data tmp這兩個目錄mount到內存裡,前提是你的內存足夠大。
*如果啟動過程中,紅色滾動條速度太快(和emulator裡的表現比較),runtime或者system_server進程CPU佔用率接近100%,那麼你可以修改一下你的framebuffer代碼中pan_display相關的函數的代碼,保證其調用返回時得到足夠的幀同步延遲時間。 據Google的swetland給我的說法是:This is usually indicative of lack of vsync/pageflip in the fb driver.The surfaceflinger believes it will be limited by the vsync rate and the startup animation depends on that.
*目前的Android的內核代碼有M3-r20和M5-r14兩個版本,這兩個版本對binder和power兩個驅動做了較大的修改,上層的文件系統和內核必須配套使用。 (另,我的板子上,M5版本可以跑起来,M3的版本會出現段錯誤,沒跑起來:(如果一個版本實在跑不起來,不妨試試別的版本)
*使用strace去跟踪問題!

There are some reference sites for research listed below:
http://wiki.droiddocs.net/Compilation_of_Android_kernel
http://benno.id.au/blog/
http://code.google.com/p/androidbmi/
http://www.anddev.org/
android內核編譯方法: http://blog.chinaunix.net/u/26691/showart_468007.html

http://code.google.com/p/android/downloads/list 下載emulator以及kernel. 下載而來的SDK可以檢視其rootfs,再對照 http://blog.chinaunix.net/u/26691/showart_468007.html or http://wiki.droiddocs.net/Compilation_of_Android_kernel 來編譯出自己的 kernel 給 emulator來掛載. kernel config file在 陈罡的blog亦可下載取得. 在上一篇 adnroid news 裡面有一些研究出來的心得與 URL 可以供參考學習.

想知道Android模擬器裏的Kernel config設定檔嗎?
命令:adb pull /proc/config.gz .
上面那行指令可以把Kernel config取出來看唷!

轉載來源:
http://rider51.wordpress.com/2008/05/13/simple-guide-for-porting-android-kernel/

Android模擬器adb命令介紹

在SDK的Tools文件夾下包含着Android模擬器操作的重要命令adb,adb的全稱為(Android Debug Bridge就是調試橋的作用。通過adb我們可以在Eclipse中方面通過DDMS來調試Android程序,說白了就是debug工具。

adb的工作方式比較特殊採用監聽Socket TCP 5554等端口的方式讓IDE和Qemu通訊,默認情況下adb會daemon相關的網絡端口,所以當我們運行Eclipse時adb進程就會自動運行。

1.通過adb可以輕鬆的執行Linux Shell命令,進入設備或模擬器的shell:adb shell就可以進入模擬器的shell環境中。也可以執行各種Linux的命令,其命令格式為:adb shell command
範例:
adb shell dir 就是列舉目錄,在Linux中根目錄為/而不是Windows上的C磁碟、D磁碟
adb shell dmesg 會列印出Linux的debug訊息。
2.安裝apk程序到模擬器則執行adb install android123.apk,這樣名為android123的安裝包就會安裝到Android模擬器中,前提是android123.apk文件需要放到SDK\Tools目錄下。
3.向emulator傳送文件,使用adb push android123.txt /tmp/android123.txt命令可以把SDK\Tools下的android123.txt文件傳輸到模擬器的/tmp/文件夾中,需要注意的是/tmp/文件夾中內容會在Android模擬器新啟動時清空。除了說明了使用ADT插件中DDMS外,如圖
4.從Android模擬器中回傳文件到電腦
通過adb pull /tmp/android123.txt android123.txt命令就會把模擬器的tmp文件夾下android123.txt文件回傳到電腦SDK\Tools目錄下。

繼續android adb命令相關介紹,除了可以方便的安裝apk文件、pc和Android模擬器互傳資料外還有Android Debug Bridge一些輔助的功能。
1.如果在Eclipse中發現有* daemon not running. starting it now *的提示可以結束adb
adb kill-server

2.顯示android模擬器狀態
adb devices 端口號等信息,執行後會顯示TCP端口號
adb get-product 獲取設備型號
adb get-serialno 獲取序列號

3.等待正在運行的設備
adb wait-for-device

4.端口轉發
adb forward adb forward tcp:5555 tcp:1234 實現將默認的TCP 5555端口轉發到1234端口上

5.查看bug報告
adb bugreport

Android - How to mount the SDCard image file to Android Emulator

(1)首先必須產生SDCard的image file
mksdcard: create a blank FAT32 image to be used with the Android emulator
usage: mksdcard [-l label]
if is a simple integer, it specifies a size in bytes
if is an integer followed by 'K', it specifies a size in KiB
if is an integer followed by 'M', it specifies a size in MiB

ex:mksdcard 1024M sdcard.iso

(2)讓SDCard連到Android Emulator
a:./emulator -sdcard ~/.android/SDK-1.0/sdcard.iso
b:用Eclipse中設定程式的Open Run Dialog裏,Target頁籤的Aditional Emulator Command Line Option中加入啟動參數 -sdcard scard.iso

(3)透過adb傳收檔案到emulator
adb push
adb pull
ex:
adb push ~/mp3/audio.mp3 /sdcard/audio.mp3

linux底下也可以使用,來管理
mount -o loop sdcard.img android_sdcard

(4)進入emulator的shell
adb shell


資料來源:
http://www.android123.com.cn/moniqi/54.html
http://www.android123.com.cn/moniqi/55.html
http://blackdidi.wordpress.com/2008/10/10/android-how-to-mount-the-sdcard-image-file-to-android-emulator/
http://www.android123.com.cn/moniqi/48.html

官方ADB文件:
http://code.google.com/android/reference/adb.html

2008年11月5日 星期三

EFI介紹之二——框架結搆(Framework)[轉貼]


這個就是Intel設計出來的一個完整的EFI BIOS示意圖,其中綠色的部分就是Framework,我現在從下至上一一介紹。

1)Hardware 這個沒有什麼好說的,就是指我們的平台,主板。

2)Framework,一個大的“H”型結搆,好像一個大的容器,兩端都能裝東西,裝入協議和接口,下端的協議用來訪問硬件,上端協議用來和操作系統進行交互,而兩端的協議進行通信的橋梁就是Framework所設計出的兩個基本模塊:DXE Foundation和PEI Foundation。之所以有兩個,是因為在BIOS過程中分兩個階段,他們各自包含了一個稱之為調度器(Dispatcher)的東西,來調度執行子模塊。這兩個Foundation里面到底有哪些東西,我會在後面的文章中繼續介紹。Framework還包含了Framework Driver,它實現了除Foundation之外的功能,比如訪問硬件的接口等等,注意,它僅僅包含接口而已,不包含接口的實現。

3)Platform Drivers,這個是和具體硬件平台相關的驅動,訪問硬件接口的實現,前一篇文章已經提到,EFI在設計的時候就考慮到跨平台,所以我們在這里看到了他把何平台有有關的東西做到了一個模塊里面,那樣在移植到其他平台的時候,只需要換掉這個部分就可以了。

注:在這里我們經常會看到"Driver"這個詞,我想說明一下,這個“Driver”和我們日常所聽到的驅動程序有點不一樣,可以翻譯成接口,我覺得更加恰當一些。

4)EFI Drivers,這個指一個符合EFI 驅動標准(EFI Driver Modle)的驅動程序,EFI的標准化甚至滲透到了驅動層面,為了兼容性,也制定了驅動標准,凡是符合此標准的程序,都可以在所有的EFI BIOS上直接運行,而不需要任何改動。這樣也給做外圍設備的廠商提供了方便,他們在編寫設備驅動程序的時候,只需要去了解驅動模型就好,而不要去研究整個EFI BIOS,也不用考慮不同的EFI BIOS會有不兼容這個問題。

5)Capatibility Support Module(CSM)為了兼容現有的匯編語言編寫的設備驅動程序和操作系統,而提供了這個部分,計算機領域都要考慮向前兼容的問題,直到BIOS的所有部分都符合EFI標准,這個模塊才會拿掉,不過現階段,這個東西還會存在很長一段時間,因為目前使用EFI BIOS的操作系統很少,Mac OS,Vista+SP1,Linux也正在准備,前景很好:),還有重要的一點,就是現階段的一些操作系統不可能很快就被淘汰,Dos到今天還在廣泛使用。

6)EFI,再往上一層黃色薄薄的一層,EFI本身所表示的就是接口,所以我們可以看到,在Framework這幅圖里面,它只占了很少的一部分,僅僅提供了OS和Framework之間的接口而已。絕大多數工作,都是在Framework中完成。

7)OS,最上面灰色的部分,這幅圖里面有兩種OS,一種是支持EFI的操作系統,另一種是傳統的(Windos XP/98,DOS等等),後者在啟動過程中還需要CSM支持,用Int 19H中斷,所以它放在了CSM的正上方,而支持EFI的操作系統,是不要CSM支持的,它的啟動方式是EFI標准所規定的,這個我們在後面繼續介紹。

資料來源:
http://blog.csdn.net/lpg123/archive/2008/08/30/2853502.aspx

2008年10月31日 星期五

關於EFI的一點介紹[轉貼]

本文主要分為如下幾個部分:
1、 EFI Overview :主要從整體上去描述一下什么是EFI。以及應用EFI對我們可能帶來的好處。
2、 Framework : 從原理,架搆等幾個方面重點介紹了EFI規範的一種標准實現Intel Platform Innovation Framework for EFI(以下簡稱Framework)。
3、 Development Tools :重點介紹了目前由AMI提供的開發工具Visual eBIOS。并簡單探討了Insyde公司的相關工具。 同時還介紹了American Arium公司提供的硬件仿真器以及調試軟件Source Point。
4、 EFI Development : Applications & Drivers :詳細探討了EFI開發的一些細節問題。并分析了一個簡單的EFI Applications。同時還探討了EFI Drivers。
5、 EFI Shell :探討了EFI Shell。

以上是主要的幾個討論方向,具體還會細分,詳見正文部分。

1、EFI Overview EFI綜述

1.1 Problems on legacy BIOS 傳統BIOS所面臨的難題
在仔細的探討EFI之前,我們先來回顧一下什么是BIOS。BIOS是英文Basic Input/Output System的縮寫,意為基本輸入輸出系統。從IBM於上世紀八十年代初推出了全世界第一台PC機開始,BIOS就成了個人計算機必備的系統軟件,用於管理基本的硬件,提供各種中斷調用,引導操作系統等等。可以看出,BIOS對於個人計算機來說,是非常重要的系統軟件,沒有BIOS的計算機是無法運行的。
傳統上的BIOS經過了長達20多年的時間,基本上沒有大的特別的改進,在操作系統已經完全32位化的今天,BIOS仍然停留在16位實模式時代。我們知道,在這個模式下,IA32架搆的CPU只能訪問1MB的基本內存,這就大大的***了程序員的創造性。同時,各種板卡上的BIOS在映射到系統內存中的時候,受到128Kbytes的大小***。這使得一台計算機不能安裝太多的板卡,否則他們的BIOS的容量將很容易突破128Kbytes,但是在一些服務器上,安裝很多板卡已經很常見。
同時由於BIOS一般選擇釆用匯編語言直接開發,使得開發入門難度很大。很多初級工程師甚至無法完成這樣的任務。并且各個公司之間的代碼不兼容,嚴重阻礙了其發展。

1.2 Intel’s solutions :EFI Intel的解決方案:EFI
EFI是Intel為了解決上述的BIOS難題而推出一項新技術,旨在向業界提供一種在未來20年內仍然可以應用的BIOS架搆。EFI是Extensible Firmware Inte***ce的縮寫。中文意思是可擴展固件接口。由於目前習慣上叫做EFI,本文將繼續稱其為EFI而不是它的中文譯名。
正如它的名字一樣,EFI并不是一套軟件,而是一整套定義的很好的接口。它是一種規範,Intel目前已經正式發布了EFI Specification Revisions 1.10。任何人都可以按照EFI寫出自己的實現來。而Intel也為我們准備好了一個標准的實現:Intel Platform Innovation Framework for EFI(以下簡稱Framework)。更加值得一提的是,Framework在BSD協議的規範下,已經實現了開放源代碼,這為我們今後開發在EFI之上的應用提供了充分的技術保障。

1.3 Benefits of the EFI EFI的優點
EFI設計的充分原則就是屏蔽掉下層的硬件,事實上通過我們的分析,EFI已經很有操作系統的味道,EFI大概包括如下的幾個部分:
1) Pre-EFI基礎代碼
2) 針對特定晶片的Pre-EFI模塊
3) DXE基礎代碼
4) DXE驅動(Framework)
5) DXE驅動(硬件)
6) 兼容性支持模塊CSM(可選,只支持IA32)
EFI通過上述的這些組成部分,提供了以往BIOS可以提供的全部功能,并且做了大量的更新。同時在容量上也做的相當的完美,可以放入4MB的Flash中。而啟動速度和喚醒速度也符合HDG標准。
更加重要的是,EFI要求使用C語言作為開發語言,這樣一來,使得我們可以更加容易的加入到BIOS的開發中來,同時也容易實現模塊化與標准化。完全模塊化的一個好處就是那些ODM或者OEM們可以方便添加他們想要的功能到EFI上。對我們而言,好處也是不言自明的,如果我們決定開始基於EFI的項目,那么我們可以在EFI中添加屬於我們自己的模塊,更加方便我們的用戶的定制,甚至為用戶提供個性化服務提供了可能。

1.4 Supported OS 操作系統系統支持情況
目前正式宣布支持EFI的操作系統包括Microsoft Windows Longhorn以及部分Linux。目前Longhorn已經進入了Beta1測試階段,Intel也在一個DEMO中展示了用EFI來引導Longhorn的實際情況。同時展示的還有如何用EFI去引導RedHat Linux(需要借助一個第三方的開源軟件ELILO)。至於其他的操作系統如Windows XP等,則暫時還不能支持EFI,所以只得使用CSM來達到引導目的。

2、 Framework

2.1 Framework Overview 架搆綜述
簡單的說,Framework就是EFI的一種實現,由Intel完成。完全實現了EFI Specification Revisions 1.10中提及的各項功能。開發者可以基於Framework,開發出各種EFI應用來。同時也可以為Framework新增新的功能。

2.2 Benefits of using the Framework 使用Framework的好處
最大的好處可以大大減輕我們的勞動量,我們只把注意力集中到最需要注意的地方上去,既然已經有了EFI的實現,并且是開源的實現,那么自然不需要我們再次實現一次。此外,Framework的其他一些好處還包括:
(1)跨平台Cross platform 目前Framework可以支持Intel IA32/64,XScale等硬件平台。同時我們注意到,Framework并不排斥其他平台,他擁有極高的可移植特性。
(2)模塊化設計Model Design Framework的所有特性都在驅動程序之中實現。而Framework本身則提供了高效的管理這些驅動程序的方法。比方說,你可以load一個驅動,而不需要重新啟動計算機;當你想更新已經load過的驅動,你只需簡單將它unload,然後再load新的驅動就可以了。對我們開發者而言,可以通過編寫我們自己的驅動,來為Framework提供我們自己需要的功能。
3) 快速的啟動時間Fast Startup Time 正如EFI要求的那樣,Framework的啟動時間非常迅速,在這一點上,它一點也不比那些傳統的BIOS落後。
此外,由於Framework是EFI的實現,所有EFI的優點應該說Framework也都具備。

2.3 The life cycle of the Framework Framework的生命周期
Framework的執行,是按照如下的順序來的:
(1)SEC :Security。這個是系統上電後立即進入的一個階段,在這一階段,Intel并沒有做太多的說明,相反,他們說這個階段可以由大家按照自己的需要利用。也就是說,我們可以安排我們自己想要的任何代碼在這個階段,比如一些身份驗證之類的,總之,SEC可以說給開發者帶來了充分的可定***務。
(2)Pre-EFI :正如它的名字一般,這個是在真正的EFI環境之前進入的一個狀態,這個狀態相當重要,以至於Intel花了很多時間來向我們闡述。根據目前的信息,這個階段大概會做很多初始化的工作,會初始化CPU,初始化主板上的一些控制器以及晶片組,更加重要的是,再這個階段內,會使用一種技巧,來迅速的建立起C 代碼的執行環境,也就是說,會建立一個堆棧。這樣,以後機器就可以運行那些由C語言編寫的軟件了。最後PEI內核會按照一種方法來加載所有的PEIM( Pre-EFI Module )。PEIM是一種可以運行在這個階段的一種模塊化的程序,任何人都可以開發自己的PEIM,加載的工作由PEIM Dispatcher完成。全部加載之後,PEI會調用DXE Main并將系統控制權交給下一個階段DXE。
(3)DXE :Driver Execution Environment。這應該說是最激動人心的階段,在這個階段,EFI真正的提供了一個類似OS一樣的東西。在上一個階段PEI,系統已經建立了C代碼執行的環境,那么從這個階段開始,所有的東西,都是用高級語言完成了。進入DXE後,會首先調用各種驅動,比如Video Driver,NIC Driver,Soundcards Driver,USB Driver,PCI Controller Driver等等。完成之後,就開始進行BOOT。而這就是下一個階段了。
(4)BDS :Boot Device Select。在這里,應該會有一個選擇,是進入OS呢?還是去執行那些EFI Applications ?選擇完成之後,就進入下一個階段了。
(5)TSL :Transient System Load。這個階段會按照在BDS階段選擇的結果來做不同的事情,如果選擇了進入OS,那么控制權會被傳遞給Final OS Loader。而如果是要去運行EFI Applications,那么控制權交給Transient OS Boot Loader,這樣就會建立起一個執行環境,之後就可以執行那些EFI Applications了。目前,應該會執行一個叫EFI Shell的程序。而傳統的BIOS Setup也被寫成一個EFI Applications。
(6)RT :Run Time。這個階段就是OS運行的階段。
(7)AL :After Life。這個是OS運行之後的階段,比如關機之類的。但OS崩潰之後也屬於這個階段,也就是說,在系統萬一崩潰之後,如果使用EFI,那么還是可以做許多事情的。
以上就是Framework的執行周期。我們可以看到,Framework除了做很多傳統BIOS的工作之外,還完成了很多其他的工作。比如建立起高級語言執行環境,調用設備驅動等等。最值得注意的是,甚至還有一個執行特定應用程序的機會。

3、Development Tools 開發工具
3.1 Overview 綜述
目前到底使用什么開發工具還不是很明晰。在這次Training上,為了給大家演示,使用的是Microsoft Visual Studio.NET 2003。不過那編譯的是一個Emulator程序,用來模擬真實的EFI環境,況且由於EFI是系統軟件,不太可能直接使用類似VS.NET這樣的高級開發工具。
後來在EFI Driver Development這門課程上,一位HP的工程師認為只要是C Compiler,理論上都可以開發。但是如果要編譯成EBC( EFI Byte Code )代碼的話,那么還得向Intel購買相應的編譯器。(我看了下價格,一個許可證是900多美金!靠!)將代碼編譯成EBC代碼的好處就是,不同的硬件架搆可以共同使用同一套最終的二進制代碼。比如,如果一個設備的Option ROM內的程序是編譯成EBC的話,那么該設備就可以不做任何的修改就可以直接支持IA32/64。而事實上,我們知道,目前Intel的64位安騰處理器與32位的Pentium4處理器在指令集上是不兼容的。那么這又是為什么呢?因為Intel在Framework內,提供了一個叫做EBC Virtual Machine Driver的Protocols。正是靠它,才實現了這樣一個功能。
不過一些第三方廠商到是展示了不少很不錯的開發工具。主要有AMI的開發工具Visual eBIOS,Insyde公司的調式工具H2ODDT,以及American Arium的硬件調式工具以及配套的軟件SourcePoint。

3.2 AMI Visual eBIOS
VEB是一款非常出色的開發工具,由著名的BIOS Venders AMI提供。他為EFI提供了大量的Value Add的工具,AMI認為,Intel的EFI僅僅只是一個骨架(Skeleton)。而他們的VEB則為其穿上了衣服,并為不同的應用目的而做了大量的個性化開發。

3.3 Insyde H2ODDT
由於Insyde公司之前很少進入中國市場,所以我們對其產品了解不深,這款軟件從介紹上看,似乎是專門用於調式Framework的。其余的詳細細節還有待繼續研討。

3.4 American Arium
American Arium是老牌的硬件調式器供應商,他們這次帶來了ECM-50與ECM-XDP。之間的差別似乎僅僅是ECM-XDP只支持那些使用XDP接口的處理器。而ECM-50則支持ITP與XDP接口。同時還演示了配套的軟件SourcePoint。從展示來看,這似乎是目前最好的調式工具,可以直接從硬件上去調式,完全跟蹤。并且支持C Source level的調試。不過根據我們猜測,這套調試工具的價格一定不菲。

4、 EFI Development : Applications & Drivers
EFI 開發 :應用程序與驅動程序

使用EFI的一個很大的好處就是有EFI Application和EFI Drivers。我下來會分別給予討論,事實上,Intel自己也認為,App和Driver在本質沒什么不同。都是一段程序,只是他們的側重點有所不同而已。App需要和用戶交互,而Driver則主要提供某種服務。

4.1 EFI Applications EFI應用程序

4.1.1 Module of the EFI Applications EFI應用程序的類型
目前有好幾種編寫EFI Applications的方法,分別是基於EFI的,基於EFI Library的,基於C Library以及基於C Standard library的。根據Intel的說法,編譯出來的應用程序的體積是一個比一個大,所以,如果想獲得最小的體積,那么就應該使用基於EFI的方法,這也是Intel所推荐的。

4.1.2 A EFI Applications based on EFI 一個基於EFI的簡單應用
最簡單的方法就是親自來看一個EFI App,下面我們就展示一個最簡單的EFI App。

#include <>

EFI_STATUS
InitializeDemoApp(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
SystemTable->ConOut->OutputString(SystemTable->ConOut, L“DEMO Application” ) ;
return EFI_SUCCESS ;
}

這個程序的工作很簡單,就是打印一個字串到終端設備上。類似於我們常見的HelloWorld。從這個程序中,我們可以看出幾點:
(1)所有基於EFI的應用成都必須包含頭文件efi.h
(2)主函數必須被申明成EFI_STATUS
(3)必須在參數前加IN或者OUT或者OPTIONAL來說明參數的類型。這些修飾符是Intel在efidef.h中定義的。目前是空的,不過為了未來的兼容性,所以還是加上的好。
可以看出,單單就入門而言,由於是使用C語言開發,所以入門門檻很低,大多數的工程師都很輕松的加入到開發隊伍中來。至於其他的編程模型,與上述的基於EFI的方法大同小異,只是所使用的庫不同,在此就不在重復了。

4.2 EFI Drivers EFI驅動程序
現在簡單的介紹一下EFI Drivers,事實上驅動程序與一般的應用沒什么區別,只不過驅動不能直接執行,而是在EFI的調度下在後台執行,同時驅動可以接觸硬件。當然也有不接觸硬件的驅動,那叫Service Driver,是用來提供某種服務的,比如EFI EBC Virtual Machine就是一種Service Driver。EFI本身提供了很強大并且的高效的管理這些驅動的方法,當需要一個驅動的時候,我們可以load它,而當有了新版本或者該驅動所管理的硬件已經不在需要的時候,我們可以很方便的unload它。Intel已經提供了一個工具,叫DWW,用他可以很方便的生成基於EFI的驅動程序模型。

5, EFI Shell

5.1 EFI Shell Overview EFI Shell綜述
什么是EFI Shell?首先它是一個基於EFI的應用程序,其次它非常類似我們在Windows中遇到的cmd或者在Linux中的shell。事實上,這就是一個操作環境,一個外殼程序,它負責接收用戶的輸入,將用戶的輸入解釋并告訴內核執行,同時將執行結果顯示出來。它完成和用戶的交互功能。所以,EFI Shell是非常重要的應用程序。

5.2 A EFI Shell Commands EFI Shell命令
EFI Shell使用字符界面和用戶交互,這里列出一些可能用到的命令以便參考。
( 1)pci : 顯示PCI設備或者PCI配置信息
( 2)mm :顯示或者修改內存,I/O以及PCI資源。
( 3)mem :顯示系統內存或者設備內存的情況。
( 4)memmap :顯示由EFI Environment創建的Memory Map。
( 5)drivers :按照EFI Driver 的類型來逐一顯示所有的已經安裝的驅動程序。
( 6)devices :顯示所有已經被EFI Driver控制的設備。
( 7)devtree :按照EFI Driver的類型來顯示設備樹。
( 8)dh :顯示在EFI Environment中的所有的Handles。
( 9)connect :將一個Driver綁定到一個設備并啟動設備。
(10)load :將一個Driver讀入內存。
(11)unload :將一個Driver從內存中卸載。
本文由xinxiaoc 發表於
http://bbs.matwav.com/post/view?bid=103&id=250675&sty=1

2008年10月21日 星期二

C++ Function Templates的基本用法

因為SystemC裏有用到template的語法,所以複習一下C++裏的Function Templates用法
底下的程式是一個取最大值的GetMax Function Templates
#include <iostream>
using namespace std;

template <class T>
T GetMax (T a, T b) {
T result;
result = (a>b)? a : b;
return (result);
}

int main () {
int i=5, j=6, k;
long l=10, m=5, n;

// k=GetMax<int>(i,j);
// n=GetMax<long>(l,m);
k=GetMax(i,j);
n=GetMax(l,m);

cout << k << endl;
cout << n << endl;
return 0;
}

T GetMax(T a,T b):T表示可以任意取代各種的資料型態,如果T是int時,代表著傳入了2個int值,回傳1個int值
上面程式執行結果如下:
6
10

思考一下:如果想要傳進1個int,1個long,這樣子可以嗎?
答案是可以做,但你必需修改你的template,改成像下面的方式宣告
template <class T, class U>
T GetMin (T a, U b) {
return (a<b?a:b);
}

而你的主程式就可以像下面的方式使用
int i,j;
long l;
//i = GetMin<int,long> (j,l);
i = GetMin (j,l);


資料來源:
http://www.cplusplus.com/doc/tutorial/templates.html

2008年10月3日 星期五

SystemC Linux開發環境配置

SystemC Linux開發環境配置

SystemC的開發工具有很多種,不過原則上SystemC只是在C++裏增加一個Class,因此基本上任何一個符合ANSI標準的C++編譯工具都可以對systemC程式進行編譯連結來產生可執行檔。
在此我們將介紹如何在Linux環境裏,使用g++來編譯SystemC的程式。我是用4.1.2的g++。

首先請到OSCI網站下載systemc-2.2.0.tgz
http://www.systemc.org/downloads/standards

下載時需要帳號才行下載,所以就申請一個,啟動帳後再回來下載
下載回來後,找個地方把你的systemc-2.2.0.tgz解開

tar xvf systemc-2.2.0.tgz
然後
cd systemc-2.2.0
然後
mkdir /usr/systemc
然後
./configure --prefix=/usr/systemc
最後
make
make install
做到這裏,我們已經把SystemC的開發環境做好了,我最後在make install時有發生錯誤,但由於是example的部份,所以我就不管它了

接下來寫個範例程式來測看看
hello.h
#ifndef _HELLO_H
#define _HELLO_H
#include "systemc.h"

SC_MODULE(hello){
SC_CTOR(hello){
cout<<"Hello,SystemC!"<<endl;
}
};
#endif
hello.cpp
#if 1

#include "hello.h"

#else
#include "systemc.h"

class hello : public sc_module{
public:
hello(sc_module_name name) : sc_module(name){
cout<<"Hello,SystemC!"<<endl;
}
};
#endif

int sc_main(int argc,char** argv){
hello h("hello");
return 0;
}
上面的hello有2種寫法,但意思是一樣的啦!

Makefile
LIB_DIR=-L/usr/systemc/lib-linux
INC_DIR=-I/usr/systemc/include
LIB=-lsystemc

APP=hello

all:
g++ -o $(APP) $(APP).cpp $(LIB_DIR) $(INC_DIR) $(LIB)

clean:
rm -rf $(APP)
你也可以使用g++ hello.cpp -I/usr/systemc/include -L/usr/systemc/lib-linux -o hello -lsystemc直接下命令編譯
參數說明:
    -I/usr/systemc/include 告訴g++去/usr/systemc/include底下尋找include檔案,也就是我們寫的systemc.h路徑
    -L/usr/systemc/lib-linux 告訴ld去/usr/systemc/lib-linux底下尋找library檔案
    -o hello 最後輸出一個執行檔,檔名為hello
    -lsystemc 靠訴ld要引用libsystemc.a這個函式庫
如果你在編譯時,發生了類似下面的錯誤時
g++ -o hello hello.cpp -L/usr/systemc/lib-linux -I/usr/systemc/include -lsystemc
hello.cpp:16: error: new types may not be defined in a return type
hello.cpp:16: note: (perhaps a semicolon is missing after the definition of 'hello')
hello.cpp:16: error: two or more data types in declaration of 'sc_main'
hello.cpp: In function 'hello sc_main(int, char**)':
hello.cpp:16: error: new declaration 'hello sc_main(int, char**)'
/usr/systemc/include/sysc/kernel/sc_externs.h:49: error: ambiguates old declaration 'int sc_main(int, char**)'
hello.cpp: In function 'hello sc_main(int, char**)':
hello.cpp:18: error: conversion from 'int' to non-scalar type 'hello' requested
make: *** [all] Error 1

千萬不要以為是sc_main參數有問題,或是回傳值有問題,其實真正的問題點很有可能是沒有加";"喔!
比如:
SC_MODULE(hello){
SC_CTOR(hello){
cout<<"Hello,SystemC!"<<endl;
}
}
就是在最後的"}"後面,還必需接一個";",才不會有錯誤。

執行結果如下:
[root@svn hello]# ./hello

SystemC 2.2.0 --- Oct 3 2008 11:10:01
Copyright (c) 1996-2006 by all Contributors
ALL RIGHTS RESERVED
Hello,SystemC!


如果是用Windows的可以參考底下這篇文章或 Installing SystemC
Systemc Setup Vc
View SlideShare document or Upload your own.



如果使用gcc 4.x以上的版本編譯systemc 2.2.0或許會遇到
make[3]: Entering directory `/home/Pratik/SystemC/systemc-2.2.0/objdir/src/sysc/utils'
g++ -I. -I. -I../../../../src/sysc/utils -I../../../../src -Wall -DSC_INCLUDE_FX -O3 -c -o sc_utils_ids.o `test -f '../../../../src/sysc/utils/sc_utils_ids.cpp' || echo '../../../../src/sysc/utils/'`../../../../src/sysc/utils/sc_utils_ids.cpp
../../../../src/sysc/utils/sc_utils_ids.cpp: In function ‘int sc_core::initialize()’:
../../../../src/sysc/utils/sc_utils_ids.cpp:110: error: ‘getenv’ is not a member of ‘std’
../../../../src/sysc/utils/sc_utils_ids.cpp:111: error: ‘strcmp’ was not declared in this scope
../../../../src/sysc/utils/sc_utils_ids.cpp: At global scope:
../../../../src/sysc/utils/sc_utils_ids.cpp:119: warning: ‘sc_core::forty_two’ defined but not used
make[3]: *** [sc_utils_ids.o] Error 1
make[3]: Leaving directory `/home/Pratik/SystemC/systemc-2.2.0/objdir/src/sysc/utils'
make[2]: *** [all-recursive] Error 1
make[2]: Leaving directory `/home/Pratik/SystemC/systemc-2.2.0/objdir/src/sysc'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/home/Pratik/SystemC/systemc-2.2.0/objdir/src'
make: *** [all-recursive] Error 1
---------------------------------------------------------------------------------
please added the following headers in systemc-2.2.0/src/sysc/utils/sc_utils_ids.cpp
#include "string.h"
#include "cstdlib"
然後再重新編譯即可


資料參考來源:
http://blog.csdn.net/funeryoung/archive/2007/11/08/1874515.aspx
http://www.ht-lab.com/howto/vh2sc_tut/vh2sc_tut1.html
一週學會SystemC
安裝SystemC 2.2

2008年10月1日 星期三

周杰倫在-稻香歌詞

周杰倫在《稻香》開頭第一段就唱,「對這個世界如果你有太多的抱怨,跌倒了就不敢繼續往前走,為什麼人要這麼的脆弱、墮落…」,提醒大家不要以抱怨代替感激,要傳達的概念就是「知足」。

  《稻 香》 詞:周杰倫 曲:周杰倫
  對這個世界如果你有太多的抱怨
  跌倒了就不敢繼續往前走
  為什麼人要這麼的脆弱 墮落
  請你打開電視看看
  多少人為生命在努力勇敢的走下去
  我們是不是該知足
  珍惜一切 就算沒有擁有
  還記得你說家是唯一的城堡 隨著稻香河流繼續奔跑
  微微笑 小時候的夢我知道
  不要哭讓螢火蟲帶著你逃跑 鄉間的歌謠永遠的依靠
  回家吧 回到最初的美好

  不要這麼容易就想放棄 就像我說的
  追不到的夢想 換個夢不就得了
  為自己的人生鮮艷上色 先把愛塗上喜歡的顏色
  笑一個吧 功成名就不是目的
  讓自己快樂快樂這才叫做意義
  童年的紙飛機 現在終於飛回我手裡
  所謂的那快樂 赤腳在田里追蜻蜓追到累了
  偷摘水果被蜜蜂給叮到怕了 誰在偷笑呢
  我靠著稻草人吹著風唱著歌睡著了
  哦 哦 午後吉它在蟲鳴中更清脆
  哦 哦 陽光灑在路上就不怕心碎
  珍惜一切 就算沒有擁有

* 還記得你說家是唯一的城堡 隨著稻香河流繼續奔跑
  微微笑 小時候的夢我知道
  不要哭讓螢火蟲帶著你逃跑 鄉間的歌謠永遠的依靠
  回家吧 回到最初的美好

資料來源:
http://www.epochtimes.com/b5/8/9/22/n2271481.htm

2008年9月26日 星期五

Android 1.0 SDK Released

更新速度真快@@
Android 1.0 SDK已經在2008/09/24釋放出來可以下載囉!
有興趣的人快去載來玩看看吧!

http://android-developers.blogspot.com/2008/09/announcing-android-10-sdk-release-1.html

0.9->1.0改變了些什麼呢?
http://code.google.com/android/migrating/0.9-1.0/changes-overview.html

  1. Content Provider

  2. Settings.System加入了許多新的Wi-Fi設定,用來提供儲存Wi-Fi的狀態。

  3. Device Hardware

  4. Camera增加了新的介面,名為Camera.ShutterCallback,用來在拍照後快門關閉後的事件回呼函式。
    SensorManager增加了一些新的fields。

  5. Maps

  6. ItemizedOverlay增加新的方法(methods)到overlays。
    MyLocationOverlay類別增加一個新的方法去偵測是否有指南針被顯示在MapView以及接收sensor位置改變時的事件。

  7. Media

  8. 在AudioManager裏加入了新的載入/卸載音效的方法。
    MediaMetadataRetriver類別在此版尚不支援。
    android.media.MediaPlayer.getFrameAt()在此版本也不支援。

  9. NetWork

  10. android.net.wifi新增了一些類別用來保持wi-fi無線喚醒及識別配對加密的WPA功能,這也改變了其它相關類別的fields及methods。
    org.apache.http.impl已被加入到這個平台

  11. Package manager
  12. 刪除使用者資料及快取(cache)的方法已經從PackageManager中移除。

  13. Text input
  14. TextKeyListener增加了release()的方法。

  15. Test

  16. 在android.test裏增加了一些新的類別及介面,如建置包含測試裝置的測試環境,測試單元及測試包。
    新增junit.runner套件。
    新增android.test.mock套件含工具類別,用來提供stub或mock來進行Android框架區塊單元的各種測試。
    新增android.test.suitebuilder套件含工具類別,用來提供test runner classes。

  17. Views

  18. 新增數個XML屬性給View物件。
    新增支援音效功能。
    你已可以監聽sensors(感知器)的改變狀態。
    WebView加入新的透明物件用來給移動的WebView跨越process的範圍。
    WebView加入新方法給managing scrollbar overlays.

  19. XML

  20. org.xmlpull.v1在這平台已可以使用

  21. Widgets

  22. verifyDrawable(Drawable)這個方法的屬性已從公開(public)改變成保護(protected).還有一些小widget有做一些變動。



1.0 Source Code下載網址
http://code.google.com/p/android/downloads/list
主要項目如下四個:
Linux 2.6.25 for Android 1.0 SDK,Release 1
Source for WebKit as included in Android 1.0 SDK, Release 1
Emulator for Android 1.0 SDK, Release 1
Android Development Tools: Plugins 0.8.0 for Eclipse 3.3/3.4, for Android SDK 1.0_r1(source only)

2008年9月25日 星期四

[轉載]斷點續傳技術

北京理工大學 20981 陳罡

要做手機斷點續傳了,網上的代碼有很多,但是多數要麼是過於復雜,要麼是用java,pascal之類的語言編寫的。都不適合直接用在手機上,無奈之下我這個懶人開始動手自己寫了。
(1)手機斷點續傳的未來?
手機上開發應用程序的時候,或多或少都要用到gprs連接互聯網(Internet),從互聯網上的服務器中把數據取出來,然後存儲到手機上,利用專門的客戶端來查看。這就可以美其名曰“在線更新”了。隨着智能手機的處理能力越來越強以及gprs升級在即(也就是傳說中的2.5G或3G了),手機的網絡應用更加惹人注意,尤其是在RSS手機新聞組、手機mail下載大附件、手機電視實時緩沖視頻流、在或者在線聽mp3、下載圖鈴之類的3G手機網絡應用上,是否具有斷點續傳的功能尤其重要。這項技術還將發展相當長的一段時間(除非移動把什麼cmwap,cmnet都統一了,目前還沒有看到有統一的跡象)。

(2)手機斷點續傳的實質?
手機上的應用越花俏就必然對應着需要下載的數據文件就會越大。目前絕大多數的手機瀏覽器,都支持gprs下載功能,所不同的是,絕大多數都沒有斷點續傳的功能。比如你要下載一首几百K的mp3,下載到2/3的時候,突然進了地鐵或者信號不好,斷開了,那就意味着剛剛的那2/3已經浪費了。再次下載的時候,就需要重新下載了。斷點續傳這個技術就是用來解決這個問題的,它的實質就是如果程序開始准備重新下載的時候,先檢查一下,已經下載了多少了,然後再接着剛剛下載過的地方繼續,接着下載。

(3)傳統斷點續傳的原理?
首先,斷點續傳不是什麼高深的技術,它是標准的http協議中已經定義了很久的東西;其次,需要服務器支持,我這邊使用的是apache的服務器,對斷點續傳支持得很好。

其實斷點續傳的原理很簡單,就是在http的請求上和一般的下載有所不同而已。
假設服務器域名為www.5mbox.com,文件名為/bbs/mp1.mp3
當web瀏覽器請求從服務器上的一個文時,所發出的請求如下:
GET /bbs/mp1.mp3 HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
excel, application/msword, application/vnd.ms-powerpoint, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
Connection: Keep-Alive


服務器收到請求以後,會回應如下內容:
200
Content-Length=106786028
Accept-Ranges=bytes
Date=Mon, 30 Apr 2001 12:56:11 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT

...後面跟着就是數據了。

斷點續傳,也就是要從文件已經下載的地方開始繼續下載。所以在客戶端瀏覽器傳給
web服務器的時候要多加一條信息--從哪里開始。
下面是用自己編的一個"瀏覽器"來傳遞請求信息給web服務器,要求從2000070字節開始。
GET /bbs/mp1.mp3 HTTP/1.0
User-Agent: NetFox
RANGE: bytes=2000070-
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

仔細看一下就會發現多了一行RANGE: bytes=2000070-
這一行的意思就是告訴服務器mp1.mp3這個文件從2000070字節開始傳,前面的字節不用傳了。
服務器收到這個請求以后,返回的信息如下:
206
Content-Length=106786028
Content-Range=bytes 2000070-106786027/106786028
Date=Mon, 30 Apr 2001 12:55:20 GMT
ETag=W/"02ca57e173c11:95b"
Content-Type=application/octet-stream
Server=Microsoft-IIS/5.0
Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT

。。。二進制數據

和前面服務器返回的信息比較一下,就會發現增加了一行:
Content-Range=bytes 2000070-106786027/106786028
返回的代碼也改為206了,而不再是200了。

知道了以上原理,就可以進行斷點續傳的編程了。

(4)斷點續傳的實現
懶得一個個解釋,直接上代碼了(請注意,下面的代碼是在win32上做驗證用的,不是給手機用的),採用c++編寫,dev-c++ 4.9.9.2編譯的,我相信g++都行vs系列自然不在話下:

M5HttpDown.h文件的內容:
#ifndef _M5_HTTP_DOWN_H_
#include <process.h>
#include <wininet.h>
#include <winsock2.h>
#include <stdio.h>
#define HTTP_DEBUG_MODE 1

#define HTTP_WEB_PORT 80
#define HTTP_TEMP_BUF_LEN 120
#define HTTP_SEND_BUF_LEN 256
#define HTTP_RECV_BUF_LEN 4096
#define HTTP_HDR_OK "200 OK"
#define HTTP_HDR_FILE_LEN "Content-Length: "
#define HTTP_HDR_DIV "\r\n"
#define HTTP_HDR_END "\r\n\r\n"
#define HTTP_PREFIX "http://"
#define HTTPS_PREFIX "https://"

// 這里分成了兩個get字符串的定義,主要是為了兼容普通的http下載
// 以及支持斷點續傳的http下載
<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #999;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%">#define HTTP_COMMON_GET &quot;GET /%s HTTP/1.1\r\n\
User-Agent: Opera 8.0\r\n\
Host: %s:%d\r\nAccept: */\
*\r\nConnection: Keep-Alive\r\n\r\n&quot;

#define HTTP_RESUME_GET &quot;GET /%s HTTP/1.1\r\n\
User-Agent: Opera 8.0\r\n\
Host: %s:%d\r\nAccept: */\
*\r\nRANGE: bytes=%d-\r\n\
Connection: Keep-Alive\r\n\r\n&quot;
</pre>

// 這里為了方便起見,就沒有用什麼notifier或者虛擬函數之類的東西了,直接回調
// recv_buf:里面裝着二進制數據,就是要下載的文件中的數據部分
// recv_len:數據的長度
// data:既然是回調函數,就需要允許caller把相關的數據結搆也帶進來。
typedef void (*RECV_CALLBACK)(char * recv_buf, int recv_len, void * data) ;

class CM5HttpDown {
protected:
// socket data
SOCKET m_sock ;
bool m_running ; // 標志是否運行
bool m_is_first_resp ; // 第一次收到數據的標志,用于跳過服務器的http頭
char * m_web_addr ; // 存放從uri中解析出來的網址
char * m_web_fname ; // 存放uri中的文件名
int m_web_port ; // uri中的服務器端口好,缺省值是80
char * m_recv_buf ; // 接收緩沖區
int m_total_bytes ; // uri中文件的總大小,單位字節
int m_recv_bytes; // 已經接收了多少字節,用于斷點續傳中接着傳

// custom defined receive handler
RECV_CALLBACK m_custom_callback ; // 回調函數指針
void * m_custom_data ; // call的自定義數據結搆指針
public:
// common receive thread func
static void recv_thread(void * data) ; // 線程函數,必須是靜態的
void recv_thread_handler() ; // 在線程函數中調用,是實際上的接收函數

protected:
// uri解析函數,能夠把諸如http://www.5mbox.com/bbs/mp1.mp3的uri分解為
// web_addr : www.5mbox.com
// web_fname : bbs/mp1.mp3
bool parse_uri(char * uri,
char * web_addr, char * web_fname, int * web_port) ;

// 第一次收到http回應的時候,解析出來文件的大小(toal_length),以及需要跳過的長度
// (jumplen),這樣就可以只把有用數據給call傳過去了,而無用的http頭就丟棄了。
bool parse_webfile_info(char * recv_buf,
int * total_length, int * jump_len) ;

// 用于把指定的field的值字符串從http頭中讀取出來,例如回應http的頭為:
// 206
// Content-Length: 106786028
// Content-Range: bytes 2000070-106786027/106786028
// Date=Mon, 30 Apr 2001 12:55:20 GMT
// ETag=W/"02ca57e173c11:95b"
// Content-Type: application/octet-stream
// Server=Microsoft-IIS/5.0
// Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT
// 該函數就可以把比方說"Content-Length: "的數據"106786028"給取出來
bool get_resp_field(char * recv_buf,
char * field_name, char * end_flag, char * res) ;

// socket的常規操作函數
bool init_sock(char * server_name, int server_port) ;
bool send_req(char * req_str, int req_len) ;
bool close_sock() ;
public:
CM5HttpDown() ;
~CM5HttpDown() ;

bool is_running() {return m_running ; }
int http_total_size() { return m_total_bytes ; }
int http_recv_size() { return m_recv_bytes ; }

// 這個就是主要的http下載函數了,使用的時候直接調用這個函數就行了。
// uri : 要下載的web文件地址,例如http://www.5mbox.com/bbs/mp1.mp3
// custom_func : 回調函數,收到二進制數據的時候,會自動調用該函數
// custom_data : call的自定義數據類型
// recv_bytes : 已經接收了多少個字節的數據,用于斷點續傳。如果該數值為0
// 則釆用普通的get方法下載;如果不為0,則釆用斷點續傳接着下載
bool http_down(char * uri,
RECV_CALLBACK custom_func, void * custom_data,
int recv_bytes = 0) ;

// 下載過程中強制關閉下載用的
bool http_stop() ;
} ;

#endif



M5HttpDown.cpp的文件內容:(作家侯杰曾經說過,源碼之下了無秘密,不需要我解釋什麼了吧。。。)
#include "M5HttpDown.h"
#include <string.h>
CM5HttpDown::CM5HttpDown()
{
m_sock = (SOCKET)(NULL) ;
m_running = false ;
m_is_first_resp = true ;
m_web_addr = NULL ;
m_web_fname = NULL ;
m_custom_callback = NULL ;
m_custom_data = NULL ;
m_total_bytes = 0 ;
m_recv_bytes = 0 ;
m_recv_buf = new char [HTTP_RECV_BUF_LEN] ;
memset(m_recv_buf, 0, HTTP_RECV_BUF_LEN) ;
}
CM5HttpDown::~CM5HttpDown()
{
if(m_recv_buf) delete [] m_recv_buf ;
if(m_web_addr) delete [] m_web_addr ;
if(m_web_fname) delete [] m_web_fname ;
}
void CM5HttpDown::recv_thread(void * data)
{
CM5HttpDown * http_down_ptr = static_cast<CM5HttpDown *>(data) ;
http_down_ptr->recv_thread_handler() ;
_endthread() ;
}
void CM5HttpDown::recv_thread_handler()
{
fd_set recv_fd ;
struct timeval tmv ;
int recv_bytes ;
int jump_length ;

while(m_running) {
FD_ZERO(&recv_fd) ;
FD_CLR(m_sock, &recv_fd) ;
FD_SET(m_sock, &recv_fd) ;
tmv.tv_sec = 1 ;
tmv.tv_usec = 0 ;
if(select(m_sock+1, &recv_fd, NULL, NULL, &tmv) < 0) {
#ifdef HTTP_DEBUG_MODE
printf("select recv failed !\n") ;
fflush(stdout) ;
#endif
return ;
}

if(FD_ISSET(m_sock, &recv_fd)) {
// time to read
recv_bytes = 0 ;
jump_length = 0 ;
memset(m_recv_buf, 0, HTTP_RECV_BUF_LEN) ;
recv_bytes = recv(m_sock, m_recv_buf, HTTP_RECV_BUF_LEN, 0) ;
if(recv_bytes > 0) {
if(m_is_first_resp) {
if(parse_webfile_info(m_recv_buf, &m_total_bytes, &jump_length)) {
// 這里比較亂,意思是:如果是斷點續傳的話,第一次收到response
// 的時候,m_recv_bytes就有數據,此時整個文件的大小應該是
// 服務器返回的content-length長度加上已經接收過了的數據長度
if(m_recv_bytes > 0) m_total_bytes += m_recv_bytes ;
#ifdef HTTP_DEBUG_MODE
printf("file length : %d\n", m_total_bytes) ;
#endif
m_recv_bytes += (recv_bytes - jump_length) ;
(*m_custom_callback)(m_recv_buf + jump_length,
recv_bytes - jump_length,
m_custom_data) ;
}
m_is_first_resp = false ;
continue ;
} else {
// common receive procdure
if((m_recv_bytes + recv_bytes) > m_total_bytes) {
recv_bytes = m_total_bytes - m_recv_bytes ;
m_recv_bytes = m_total_bytes ;
} else {
m_recv_bytes += recv_bytes ;
}
(*m_custom_callback)(m_recv_buf, recv_bytes, m_custom_data) ;
}
} else if(recv_bytes == 0) {
// conn down
#ifdef HTTP_DEBUG_MODE
printf("disconn...\n") ;
#endif
m_running = false ;
}
}
}
}
bool CM5HttpDown::send_req(char * req_str, int req_len)
{
fd_set send_fd ;
struct timeval tmv ;
int send_bytes ;
if(!m_sock || req_len <= 0 || req_str == NULL) return false ;

FD_ZERO(&send_fd) ;
FD_CLR(m_sock, &send_fd) ;
FD_SET(m_sock, &send_fd) ;
tmv.tv_sec = 1 ;
tmv.tv_usec = 0 ;
if(select(m_sock+1, NULL, &send_fd, NULL, &tmv) < 0) {
#ifdef HTTP_DEBUG_MODE
printf("select send failed !\n") ;
fflush(stdout) ;
#endif
return false ;
}

if(FD_ISSET(m_sock, &send_fd)) {
send_bytes = send(m_sock, req_str, req_len, 0) ;
if(req_len != send_bytes) return false ;
return true ;
}
return false ;
}
bool CM5HttpDown::parse_uri(char * uri, char * web_addr,
char * web_fname, int * web_port)
{
char * ptr_a = NULL ;
char * ptr_b = NULL ;

*web_port = HTTP_WEB_PORT ;
if(!uri) return false ;
// search for http or https prefix
ptr_a = uri ;
if(!strncmp(ptr_a, HTTP_PREFIX, strlen(HTTP_PREFIX)))
ptr_a = uri + strlen(HTTP_PREFIX) ;
else if(!strncmp(ptr_a, HTTPS_PREFIX, strlen(HTTPS_PREFIX)))
ptr_a = uri + strlen(HTTPS_PREFIX) ;
// get web_addr without "http://" or "https://" prefix
ptr_b = strchr(ptr_a, '/');
if(ptr_b) {
memcpy(web_addr, ptr_a, strlen(ptr_a) - strlen(ptr_b));
if(ptr_b + 1) {
// get web file name
memcpy(web_fname, ptr_b + 1, strlen(ptr_b) - 1);
web_fname[strlen(ptr_b) - 1] = '\0' ;
}
} else memcpy(web_addr, ptr_a, strlen(ptr_a)) ;
if(ptr_b) web_addr[strlen(ptr_a) - strlen(ptr_b)] = '\0' ;
else web_addr[strlen(ptr_a)] = '\0' ;
// search for uri port number
ptr_a = strchr(web_addr, ':') ;
if(ptr_a) *web_port = atoi(ptr_a + 1);
else *web_port = HTTP_WEB_PORT ;
return true ;
}
bool CM5HttpDown::get_resp_field(char * recv_buf, char * field_name, char * end_flag, char * res)
{
char * start_ptr = NULL ;
char * end_ptr = NULL ;

start_ptr = strstr(recv_buf, field_name) ;
if(start_ptr == NULL) return false ;

start_ptr += strlen(field_name) ;
end_ptr = strstr(start_ptr, end_flag) ;

if(end_ptr == NULL) return false ;
memcpy(res, start_ptr, end_ptr - start_ptr) ;
res[end_ptr - start_ptr] = '\0' ;
return true ;
}
bool CM5HttpDown::parse_webfile_info(char * recv_buf, int * file_length, int * jump_len)
{
char tmp_str[50] ;
char * offset_str = NULL ;

#ifdef HTTP_DEBUG_MODE
printf("%s\n", recv_buf) ;
#endif
// get file length
if(!get_resp_field(recv_buf, HTTP_HDR_FILE_LEN, HTTP_HDR_DIV, tmp_str))
return false ;

*file_length = atoi(tmp_str) ;

// get current offset
offset_str = strstr(recv_buf, HTTP_HDR_END) ;
if(offset_str == NULL) return false ;
*jump_len = (int)(offset_str + strlen(HTTP_HDR_END) - recv_buf) ;
return true ;
}
bool CM5HttpDown::init_sock(char * server_name, int server_port)
{
struct sockaddr_in sock_in ;
struct hostent * he ;
{
// only worked in dos
WSADATA wsadata ;
if (WSAStartup(0x0202, &wsadata) != 0) return false ;
}

// get server ip address
he = gethostbyname(server_name) ;
if (!he) sock_in.sin_addr.s_addr = inet_addr(server_name) ;
else {
sock_in.sin_addr.s_addr = *(unsigned long *)(he->h_addr_list[0]) ;
#ifdef HTTP_DEBUG_MODE
printf("ip : %s\n", inet_ntoa(sock_in.sin_addr)) ;
#endif
}

sock_in.sin_family = AF_INET;
sock_in.sin_port = htons(server_port);
m_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) ;

if(!connect(m_sock,(struct sockaddr *)(&sock_in), sizeof(sock_in))) {
HANDLE thread_handle ;
m_running = true ;
thread_handle = (HANDLE)(_beginthread(CM5HttpDown::recv_thread, 0,(void *)(this))) ;
return true ;
}
return false ;
}
bool CM5HttpDown::close_sock()
{
if(m_running) {
m_running = false ;
Sleep(1000) ;
if(m_sock) closesocket(m_sock) ;
}
{
// only worked in dos
WSACleanup() ;
}
return true ;
}
bool CM5HttpDown::http_down(char * uri,
RECV_CALLBACK custom_func, void * custom_data,
int recv_bytes)
{
char buffer[HTTP_SEND_BUF_LEN] ;
memset(buffer, 0, HTTP_TEMP_BUF_LEN) ;

if(uri == NULL) return false ;
m_recv_bytes = recv_bytes ;
m_custom_callback = custom_func ;
m_custom_data = custom_data ;

m_web_addr = new char [HTTP_TEMP_BUF_LEN] ;
m_web_fname = new char [HTTP_TEMP_BUF_LEN] ;

memset(m_web_addr, 0, HTTP_TEMP_BUF_LEN) ;
memset(m_web_fname, 0, HTTP_TEMP_BUF_LEN) ;

parse_uri(uri, m_web_addr, m_web_fname, &m_web_port) ;
if(m_recv_bytes == 0) {
snprintf(buffer, HTTP_SEND_BUF_LEN, HTTP_COMMON_GET,
m_web_fname, m_web_addr, m_web_port) ;
} else {
snprintf(buffer, HTTP_SEND_BUF_LEN, HTTP_RESUME_GET,
m_web_fname, m_web_addr, m_web_port, m_recv_bytes) ;
}
#ifdef HTTP_DEBUG_MODE
printf("%s\n", buffer) ;
#endif
m_running = true ;
if(!init_sock(m_web_addr, m_web_port)) return false ;

// send the request
return send_req(buffer, strlen(buffer)) ;
}
bool CM5HttpDown::http_stop()
{
return close_sock() ;
}


貼上整個dev-c++的工程,感興趣的朋友直接下了玩玩。
nettest.rar
運行nettest.exe,就會在其當前目錄生成一個叫test.mp3的文件,螢幕上還顯示下載進度。
可以隨時關閉,然後再次打開,看看斷點續傳的效果。
下面的這個是將上述代碼,通過RSocket移植到symbian s60 2nd平台上的測試程序(我做了一些修改使之可以同時支持cmwap和cmnet的gprs環境,再此鄙視一下移動的行為),
出于公司的利益考慮,就不開放代碼了。
NetTestSIS.rar
當調用cmwap conn的時候,接入點要選擇"移動夢網"或者"nokia.com";
當調用cmnet conn的時候,接入點要選擇"gprs連接互聯網";
看到螢幕上顯示"connected"的時候,選擇"resume",就會開始斷點續傳過程。
對於cmwap由於移動有推送頁面,在程序里面加入了效驗,如果有推送頁面,
程序會顯示"check failed",這時,再按一次"resume"即可。
更換連網方式之前,需要選擇"stop",斷開gprs連接,然後再連。
還有一個小小的意外是對于移動的網關來說,Content-Length這是標准http服務器返回的;
但是移動的代理返回的結果是Content-length這個"l"是小寫的,這個細節一定要注意才行喔!!

呵呵,期待5mbox網路版早日成功。

資料來源:
http://blog.chinaunix.net/u/26691/showart_433628.html
http://blog.chinaunix.net/u/26691/showart_433631.html

一個小故事讓我們明白資金流通的意義

“又是炎熱小鎮慵懶的一天。太陽高掛,街道無人,每個人都債台高築,靠信用度日。這時,從外地來了一位有錢的旅客,他進了一家旅館,拿出一張1000 元鈔票放在櫃檯,說想先看看房間,挑一間合適的過夜,就在此人上樓的時候---- 店主抓了這張1000 元鈔,跑到隔壁屠戶那裡支付了他欠的肉錢...