《Android深度探索(卷1):HAL与驱动开发》——6.4节使用多种方式测试Linux驱动...
本節書摘來自異步社區《Android深度探索(卷1):HAL與驅動開發》一書中的第6章,第6.4節使用多種方式測試Linux驅動,作者李寧,更多章節內容可以訪問云棲社區“異步社區”公眾號查看
6.4 使用多種方式測試Linux驅動
Android深度探索(卷1):HAL與驅動開發
在上一節已經實現了一個簡單的Linux驅動程序,該驅動的功能是統計給定字符串中的單詞數,并且在最后已經將該Linux驅動的源代碼成功編譯成動態Linux驅動模塊word_count.ko。下一步就是測試該模塊。測試的方法很多,最常用的就是直接在Ubuntu Linux中測試。當然,這對于本章實現的Linux驅動是沒問題的,但是對于需要直接訪問硬件的驅動在Ubuntu Linux上測試就不太方便。在這種情況下就需要在相應的硬件上進行測試。
對于一個Linux驅動程序,一開始可以在Ubuntu Linux上做前期開發和測試。對于訪問硬件的部分也可以在Ubuntu Linux用軟件進行模擬。當基本開發完成后,就需要在開發板或工程樣機上使用真實的硬件進行測試。當然,最后還需要在最終銷售的手機上進行測試。最終測試通過,Linux驅動才能算真正開發完成。在開發Linux驅動的過程中一個重要的步驟就是測試。本節將結合實際的開發流程介紹在不同平臺上測試Linux驅動程序。這些測試平臺包括Ubuntu Linux、Android模擬器和S3C6410開發板。
6.4.1 使用Ubuntu Linux測試Linux驅動
本節將介紹如何在Ubuntu Linux下測試驅動程序。由于上一節編寫的Linux驅動程序通過4個字節從設備文件(/dev/wordcount)返回單詞數,所以不能使用cat命令測試驅動程序(cat命令不會將這4個字節還原成int類型的值顯示)。但可以使用如下命令從日志中查看單詞數。
執行上面的命令后,如果輸出如圖6-13所示白框中的信息,說明驅動程序成功統計了單詞數。
雖然使用echo和dmesg命令可以測試Linux驅動程序,但這種方式并不是真正的測試。為了使測試效果更接近真實環境,一般需要編寫專門用于測試的程序。本節將為word_count驅動編寫一個專門的測試程序(test_word_count.c)。test_word_count.c通過直接操作/dev/wordcount設備文件與word_count驅動進行交互。測試程序的代碼如下:
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h>#include <string.h> int main(int argc, char *argv[]) {int testdev; // 打開設備文件(/dev/wordcount)的句柄unsigned char buf[4]; // 表示單詞數的4個字節// 打開設備文件testdev = open("/dev/wordcount", O_RDWR);// 如果open函數返回-1,表示打開設備文件失敗if (testdev == -1){printf("Cann't open file \n");return 0;}// 如果test_word_count后面跟有命令行參數,程序會將第1個參數值當作待統計的字符串// 如果沒有命令行參數,則只讀取設備文件中的值if (argc > 1){// 向設備文件寫入待統計的字符串write(testdev, argv[1], strlen(argv[1]));// 輸出待統計的字符串printf("string:%s\n", argv[1]);}// 讀取設備文件中的單詞數(4個字節)read(testdev, buf, 4);int n = 0; // 單詞數// 將4個字節還原成int類型的值n = ((int) buf[0]) << 24 | ((int) buf[1]) << 16 | ((int) buf[2]) << 8| ((int) buf[3]);// 分別輸出從設備文件獲取的4個字節的值printf("word byte display:%d,%d,%d,%d\n", buf[0], buf[1], buf[2], buf[3]);// 輸出統計出的單詞數printf("word count:%d\n", n);// 關閉設備文件close(testdev);return 0; }
test_word_count程序可以跟1個命令行參數(多個命令行參數只會使用第1個命令行參數)。如果命令行參數值含有空格,需要使用單引號(')或雙引號(")將參數值括起來。可以使用下面的一組命令測試word_count驅動程序。
# gcc test_word_count.c -o test_word_count # test_word_count # test_word_count "I love you."執行上面的命令后,如果輸出如圖6-14所示的信息(假設word_count以前統計過一個含有4個單詞的字符串),表示word_count驅動成功測試。
6.4.2 在Android模擬器上通過原生(Native)C程序測試Linux驅動
雖說我們開發的是Linux驅動,但本書主要介紹的是Android版的Linux內核,因此,Linux驅動只在Ubuntu Linux上測試成功還不能保證在Android設備上一定能正常工作,所以必須在Android設備上進行測試。Android設備有很多種類,如安裝了Android的開發板、運行Android系統的手機或平板電腦等。但離我們最近的并不是這些硬件設備,而是Android模擬器。Android模擬器可以模擬絕大多數真實的環境,所以可以利用Android模擬器測試Linux內核。
在Android模擬器上測試Linux驅動首先應該想到的,也是最先應該做的就是將word_count.ko驅動模塊安裝在模擬器上。可能讀者使用過adb shell命令。如果進入Android模擬器的命令提示符為“#”,說明通過命令行方式進入Android模擬器直接就是root權限(命令提示符為“$”,表示非root權限),因此從理論上可以使用insmod命令將word_count.ko驅動模塊直接安裝在Android模擬器中。現在我們來測試一下,看看是否可以將word_count.ko安裝在Android模擬器上。現在執行build.sh腳本,并選擇“Android模擬器”,腳本會自動將word_count.ko文件上傳到Android模擬器的/data/local目錄,并進行安裝。如果讀者選擇的是S3C6410開發板,在安裝word_count.ko時就會輸出如下的錯誤信息,表示編譯Linux驅動的Linux內核版本與當前Android模擬器的版本不相同,無法安裝。所以在編譯Linux驅動時,必須選擇與當前運行的Linux內核版本相同的Linux內核進行編譯,否則就無法安裝Linux驅動。
insmod: init_module ‘/data/local/word_count.ko’ failed(Function not implemented)注意
建議上傳文件到Android模擬器或開發板時,將文件放到/data/local目錄,系統很多其他的目錄,如/system/bin,都是只讀的,除非將word_count.ko文件打包進system.img,否則無法向這些目錄寫數據,即使有root權限也不行。
用于Android模擬器的goldfish內核默認不允許動態裝載Linux驅動模塊,因此需要在編譯Linux內核之前執行如下命令配置Linux內核。
執行上面的命令后,會出現如圖6-15所示的設置界面。按空格鍵將第二項“Enable loadable module support”選中(前面是[*]),然后按回車鍵進入子菜單,選中前3項,如圖6-16所示,否則Linux驅動模塊仍然無法安裝和卸載。當退出設置菜單時保持設置。最后按節的方法重新編譯Linux內核,成功編譯內核后,Android模擬器可以使用新生成的zImage內核文件動態裝載Linux驅動模塊。
現在執行build.sh腳本文件完成對word_count驅動的編譯、上傳和安裝的工作,然后進入Android模擬器的終端,使用echo和dmesg命令可以測試word_count驅動和查看測試結果,方法與上一節相同。
注意
編譯可在Android模擬器上運行的Linux驅動模塊要使用goldfish內核,使用其他的內核編譯word_count.c,安裝時會出現如下錯誤。
insmod: error inserting 'word_count.ko': -1 Invalid module format
在Android模擬器上不僅可以使用Linux命令測試驅動,也可以像Ubuntu Linux一樣使用本地C/C++程序進行測試。可能有的讀者要問,Android不是只能運行由Java編寫的APK程序嗎?頂多是在APK程序中嵌入NDK代碼。還能直接運行普通的Linux程序嗎?答案是肯定的。不過要滿足如下兩個條件。
Android模擬器、開發板或手機需要有root權限。
可執行文件需要使用交叉編譯器進行編譯,以便支持ARM處理器。
現在使用交叉編譯器來編譯在上一節編寫的test_word_count.c文件。為了使編譯步驟盡可能簡單,我們使用Android.mk設置編譯參數,并使用make命令進行編譯。首先在/root/drivers/ch06/word_count目錄中建立一個Android.mk文件,并輸入如下的內容。
Android.mk文件中有如下兩個地方需要說明一下。
LOCAL_MODULE_TAGS表示當前工程(Android.mk文件所在的目錄)在什么模式下編譯。如果設為optional,表示不考慮模式,也就是說,在任何模式下都會編譯。該變量可以設置的值有user、userdebug、eng、optional。其中eng是默認值。
user:限制用戶對Android系統的訪問,適合于發布產品。
userdebug:類似于user模式,但擁有root訪問權限,并且可以從日志中獲取大量的調試信息。
eng:一般在開發的過程中設置該模式。除了擁有userdebug的全部功能外,還會帶有大量的調試工具。
LOCAL_MODULE_TAGS的值與TARGET_BUILD_VARIANT變量有關。TARGET_BUILD_VARIANT變量用于設置當前的編譯模式,可設置的值包括user、userdebug和eng。如果想改變編譯模式,可以在編譯Android源代碼之前執行如下命令。
其中full表示建立的目標,除了full目標(為所有的平臺建立)外,還有專門為x86建立的full-x86。詳細的建立目標執行lunch命令后就會列出。在圖4-8已經顯示了Android4支持的建立目標的編譯模式。讀者可以到第4章查看該圖。
include $(BUILD_EXECUTABLE)BUILD_EXECUTABLE表示建立可執行的文件。可執行文件路徑是< Android源代碼目錄 >/ out/target/product/generic/system/bin/test_word_count。如果想編譯成動態庫(.so)文件,可以使用include $(BUILD_SHARED_LIBRARY)。動態庫的路徑是< Android源代碼目錄 >/ out/target/product/ generic/system/lib/test_word_count.so。如果想編譯成靜態庫(.a)文件,可以使用include $(BUILD_STATIC_LIBRARY)。靜態庫的路徑是< Android源代碼目錄 >/ out/target/product/generic/ obj/STATIC_LIBRARIES/test_word_count_intermediates/test_word_count.
為了將test_word_count.c文件編譯成可在Android模擬器上運行的可執行程序,可以將word_count目錄復制到< Android源代碼目錄 >的某個子目錄,也可以在< Android源代碼目錄 >目錄中為word_count目錄建立一個符號鏈接。為了方便,我們采用如下命令為word_count目錄在< Android源代碼目錄 >/development目錄建立一個符號鏈接(假設Android源代碼的目錄是/sources/android/android4/development/word_count)。
# ln -s /root/drivers/ch06/word_count /sources/android/android4/ development/ word_ count 現在進入/sources/android/android4目錄,執行下面的命令初始化編譯命令。# source ./build/envsetup.sh 可以使用下面兩種方法編譯test_word_count.c。(1)進入/sources/android/android4/development/word_count目錄,并執行如下的命令。# mm (2)在/sources/android/android4目錄下執行如下的命令。# mmm development/word_count成功編譯后可以在< Android源代碼目錄 >/out/target/product/generic/system/bin目錄中找到test_word_count文件。在隨書光盤和模擬環境中已經帶了編譯好的test_word_count程序(包括Emulator版本和Ubuntu Linux版本),可執行程序一般不需要考慮Linux內核的版本,用交叉編譯器編譯的支持ARM處理器的程序既可以在Android模擬器上運行,也可以在S3C6410開發板或其他有root權限的手機中運行。
現在執行下面的命令將test_word_count文件上傳到Android模擬器。
# adb push ./emulator/test_word_count /data/local 然后進入Android模擬器的終端,并執行下面的命令測試word_count驅動(需要先使用chmod命令設置test_word_count的可執行權限)。# chmod 777 /data/local/test_word_count # /data/local/test_word_count # /data/local/test_word_count 'a bb ccc ddd eee'執行上面的命令后,如果輸出的單詞個數是5,表示程序測試成功。
6.4.3 使用Android NDK測試Linux驅動
在Android系統中Linux驅動主要的使用者是APK程序。因此,Linux驅動做完后必須要用APK程序進行測試才能說明Linux驅動可以正常使用。由于上一節在Android虛擬機上使用C語言編寫的可執行程序測試了Linux驅動,因此很容易想到可以利用Android NDK來測試Linux驅動,
由于Android NDK也使用C/C++來編寫程序,因此可以利用上一節的C語言代碼,當然,還得加上一些Android NDK特有的代碼。在使用Android NDK測試Linux驅動之前需要做如下兩件事。
由于Linux驅動模塊不會隨Android系統啟動而裝載,因此必須執行build.sh腳本文件安裝word_count驅動。
不能使用默認方式啟動Android模擬器,而要使用我們自己編譯的Linux內核啟動Android模擬器,啟動模擬器的命令如下:
為了方便,讀者也可以在隨書光盤的Ubuntu Linux虛擬環境中直接執行如下的命令來啟動Android模擬器。其中emulator.sh文件在/root/drivers目錄中。
# sh emulator.shword_count_ndk工程的代碼部分由WordCountNDKTestMain.java和ndk_test_word_count.c文件組成。工程結構如圖6-17所示。
ndk_test_word_count.c文件用于訪問word_count驅動。該文件包含兩個供Java訪問的函數,分別用來讀取/dev/wordcount設備文件中的單詞數和向/dev/wordcount設備文件寫入字符串。下面先看看ndk_test_word_count.c文件的完整代碼。
#include <string.h> #include <jni.h> #include <fcntl.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdlib.h> // JNI函數:readWordCountFromDev // 用于從/dev/wordcount設備文件讀取單詞數 jint Java_mobile_android_word_count_ndk_WordCountNDKTestMain _readWordCountFromDev(JNIEnv* env, jobject thiz) {int dev; // open函數打開/dev/wordcount設備文件后返回的句柄,打開失敗返回-1jint wordcount = 0; // 單詞數unsigned char buf[4]; // 以4個字節形式存儲的單詞數// 以只讀方式打開/dev/wordcount設備文件dev = open("/dev/wordcount", O_RDONLY);// 從dev/wordcount設備文件中讀取單詞數read(dev, buf, 4);int n = 0; // 存儲單詞數的int類型變量// 將由4個字節表示的單詞數轉換成int類型的值n = ((int) buf[0]) << 24 | ((int) buf[1]) << 16 | ((int) buf[2]) << 8 | ((int) buf[3]);// 將int類型的單詞數轉換成jint類型的單詞數wordcount = (jint) n;// 關閉/dev/wordcount設備文件close(dev);// 返回單詞數return wordcount; } // 將jstring類型的值轉換成char *類型的值 char* jstring_to_pchar(JNIEnv* env, jstring str) {char* pstr = NULL;// 下面的代碼會調用Java中的String.getBytes方法獲取字符串的字節數// 獲取java.lang.String類jclass clsstring = (*env)->FindClass(env, "java/lang/String");// 將字符串“utf-8”轉換成jstring類型的值jstring strencode = (*env)->NewStringUTF(env, "utf-8");// 獲取java.lang.String.getBytes方法jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");// 調用String.getBytes方法將str變量的值轉換成jbytearray類型的值jbyteArray byteArray = (jbyteArray)( (*env)->CallObjectMethod(env, str, mid, strencode));// 獲取字節長度jsize size = (*env)->GetArrayLength(env, byteArray);// 將jbytearray類型的值轉換成jbyte*類型的值jbyte* pbyte = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);if (size > 0){// 為char*類型變量pstr分配空間pstr = (char*) malloc(size);// 將pbyte變量中的值復制到pstr變量中memcpy(pstr, pbyte, size);}// 返回轉換后的值return pstr; } // JNI函數:writeStringToDev // 用于向/dev/wordcount設備文件寫入字符串 void Java_mobile_android_word_count_ndk_WordCountNDKTestMain_writeStringToDev(JNIEnv* env, jobject thiz, jstring str) {int dev; // open函數打開/dev/wordcount設備文件后返回的句柄,打開失敗返回-1// 以只寫方式打開/dev/wordcount設備文件dev = open("/dev/wordcount", O_WRONLY);// 將jstring類型字符串轉換成char* 類型的值char* pstr = jstring_to_pchar(env, str);if (pstr != NULL){// 向/dev/wordcount設備文件寫入字符串write(dev,pstr, strlen(pstr));}// 關閉/dev/wordcount設備文件close(dev); }編寫上面的代碼有一個重點就是jstring_to_pchar函數。該函數可以將jstring類型的數據轉換成char類型的數據。轉換的基本思想就是調用Java方法String.getBytes,獲取字符串對應的字節數組(jbyteArray)。由于write函數需要的是char 類型的數據,因此,還必須將jbyteArray類型的數據轉換成char 類型的數據。采用的方法是,先將jbyteArray類型的數據轉換成jbyte類型的數據,然后調用memcpy函數將jbyte類型的數據復制到使用malloc函數分配的char 指針空間中。在jstring_to_pchar函數中有如下的一行代碼。
jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes","(Ljava/lang/ String;) [B"]);看到getMethodID方法最后一個參數的值是"(Ljava/lang/String;)[B",可能Android NDK初學者會對此感到困惑,以為是寫錯了。實際上這是JNI(Android NDK程序實際上就是遵循JNI規則的程序)對方法參數和返回類型的描述。在JNI程序中為了方便描述Java數據類型,將簡單類型使用了一個大寫英文字母表示,如表6-1所示。
在JNI中調用Java方法需要指定方法參數和返回值的數據類型。在JNI中的格式如下:
"(參數類型)返回值類型"
getBytes方法的參數類型是String,根據表6-2的描述,String類型中JNI的描述符是" Ljava/lang/String; "。getBytes方法的返回值類型是byte[]。這里就涉及一個數組的表示法。在JNI中數組使用左中括號([)表示,后面是數組中元素的類型。每一維需要使用一個“[”。byte[]是一維字節數組,所以使用"[B"表示。如果是byte[][][],應使用"[[[B"表示。如果Java方法未返回任何值(返回值類型是void),則用V表示。如void mymethod(int value)的參數和返回值類型可表示為"(I)V"。
Android NDK程序還需要一個Android.mk文件,代碼如下:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := ndk_test_word_count LOCAL_SRC_FILES := ndk_test_word_count.cinclude $(BUILD_SHARED_LIBRARY) 注意為了方便讀者在Eclipse中開發Android應用程序,本節的例子采用了節的方法進行配置。詳細的配置信息請讀者查看隨書光盤或虛擬環境中的例子。虛擬環境中的所有配置和目錄位置與筆者寫作本書時使用的Ubuntu Linux的環境完全相同,讀者可直接運行程序。但在隨書光盤中的例子需要將相關的路徑修改成讀者自己機器上的路徑。當然,如果恰巧讀者機器的環境與筆者完全相同,就不需要做任何修改了。
在編寫Java代碼調用JNI函數之前,先看一下本例的界面,如圖6-18所示。
讀者需要先在PC上運行build.sh腳本文件安裝word_count驅動。然后單擊“從/dev/wordcount讀取單詞數”按鈕,會在按鈕下方輸出當前/dev/wordcount設備文件中統計出的單詞數。讀者也可以在輸入框中輸入一個由空格分隔的字符串,然后單擊“向/dev/wordcount寫入字符串”按鈕,再單擊“從/dev/wordcount讀取單詞數”按鈕,就會統計出字符串中包含的單詞數,效果如圖6-19所示。
下面看一下本例中Java部分(WordCountNDKTestMain.java)的完整代碼。
package mobile.android.word.count.ndk;import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast;public class WordCountNDKTestMain extends Activity {private TextView tvWordCount;private EditText etString;@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);tvWordCount = (TextView) findViewById(R.id.textview_wordcount);etString = (EditText) findViewById(R.id.edittext_string);}// “從/dev/wordcount讀取單詞數”按鈕的執行代碼public void onClick_ReadWordCountFromDev(View view){// 顯示單詞數tvWordCount.setText("單詞數:" + String.valueOf(readWordCountFromDev()));} // “向/dev/wordcount寫入字符串”按鈕的執行代碼public void onClick_WriteStringToDev(View view){// 向/dev/wordcount設備文件寫入字符串writeStringToDev(etString.getText().toString());Toast.makeText(this, "已向/dev/wordcount寫入字符串", Toast.LENGTH_LONG).show();}// native方法public native int readWordCountFromDev();public native void writeStringToDev(String str);static{System.loadLibrary("ndk_test_word_count");} }
WordCountNDKTestMain.java中的代碼只是簡單地調用了JNI函數來操作/dev/wordcount文件。其他的代碼都是常規的Android應用級別的代碼。如果讀者對這部分不熟悉,可以參閱筆者所著的《Android開發權威指南》。
6.4.4 使用Java代碼直接操作設備文件來測試Linux驅動
如果Android擁有root權限,完全可以直接使用Java代碼操作/dev/wordcount設備文件(沒有root權限,Linux驅動模塊是無法安裝的)。本節將介紹如何使用Java代碼來測試Linux驅動(測試程序不使用一行C/C++代碼)。本節示例的路徑如下。
word_count_java工程中只有一個源代碼文件WordCountJavaTestMain.java。該文件的內容如下:
package mobile.android.word.count.java;import java.io.FileInputStream; import java.io.FileOutputStream; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast;public class WordCountJavaTestMain extends Activity {private TextView tvWordCount;private EditText etString;@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);tvWordCount = (TextView) findViewById(R.id.textview_wordcount);etString = (EditText) findViewById(R.id.edittext_string);}// “從/dev/wordcount讀取單詞數”按鈕的執行代碼public void onClick_ReadWordCountFromDev(View view){// 顯示單詞數tvWordCount.setText("單詞數:" + String.valueOf(readWordCountFromDev()));} // “向/dev/wordcount寫入字符串”按鈕的執行代碼public void onClick_WriteStringToDev(View view){// 向/dev/wordcount設備文件寫入字符串writeStringToDev(etString.getText().toString());Toast.makeText(this, "已向/dev/wordcount寫入字符串", Toast.LENGTH_LONG).show();}// 下面是用Java實現的操作/dev/wordcount設備文件的代碼// 讀取/dev/wordcount設備文件中的單詞數private int readWordCountFromDev(){int n = 0;byte[] buffer = new byte[4];try{// 打開/dev/wordcount設備文件FileInputStream fis = new FileInputStream("/dev/wordcount");// 從設備文件中讀取4個字節 fis.read(buffer);// 將4個字節轉換成int類型的值n = ((int) buffer[0]) << 24 | ((int) buffer[1]) << 16| ((int) buffer[2]) << 8 | ((int) buffer[3]); fis.close();}catch (Exception e){}return n;}// 向/dev/wordcount設備文件中寫入字符串private void writeStringToDev(String str){try{// 打開/dev/wordcount設備文件FileOutputStream fos = new FileOutputStream("/dev/wordcount");// 寫入字符串fos.write(str.getBytes("iso-8859-1"));fos.close();}catch (Exception e){}} }本例的運行效果和使用方法與上一節的例子類似。讀者可以運行隨書光盤或虛擬環境中的例子與上一節的例子進行比較。
6.4.5 使用S6410開發板測試Linux驅動
前面幾節使用了不同方法來測試word_count驅動,但歸根結底都是在PC上進行測試。那么本節將換一種平臺來測試word_count驅動。當然,如果讀者有Android手機的相應Linux內核源代碼,也可以使用本節的方法在手機上測試word_count驅動。
6.4.2節、6.4.3節和6.4.4節中的例子都可以在S3C6410開發板上運行(有的需要重新編譯,有的可以直接運行)。下面就挨個介紹如何使其在S3C6410開發板上運行。
首先應打開S3C6410開發板的電源開關,然后使用USB數據線連接S3C6410開發板和PC。最后執行build.sh腳本文件將word_count驅動安裝在S3C6410開發板上。
1.在S6410開發板上使用可執行程序測試Linux驅動
由于在S3C6410開發板運行的是Android 2.3.4,因此,需要在Android 2.3.4下使用6.4.2節的方法重新編譯test_word_count.c文件。然后將編譯好的test_word_count程序上傳到開發板。測試的方法與Android模擬器相同。
注意
本書的主題之一就是介紹如何將Android移植到不同的硬件上。那么使用test_word_count在不同硬件平臺上運行實際上也是一種移植,只不過這種移植并不是移植操作系統,而是移植應用程序,所有可稱為應用程序移植。最簡單的應用程序移植就是將應用程序源代碼編譯成可在不同目標平臺運行的二進制文件。當然,如果恰巧這些平臺中都包含應用程序所使用的API,那么直接在不同平臺編譯即可(有時需要使用交叉編譯器)。但不幸的是,在很多時候,并不是所有的API在各個平臺都有。有的API可能名字變化了,但有的API在某些平臺根本就沒實現。面對這樣的情況,一般需要先移植這些API,然后再移植應用程序。應用程序移植在Android系統中也會經常發生,如果某些特殊的Android系統(基于ARM芯片)需要一些用C語言實現的Library或可執行程序,但Android平臺并沒有這些功能,而其他平臺(如Ubuntu Linux)有這樣的程序,完全可以修改并重新編譯成ARM平臺的目標文件放到Android系統中。
2.在S6410開發板上使用Android NDK測試Linux驅動
在Eclipse中重新編譯節編寫的Android NDK程序就可以在S3C6410開發板上運行,測試方法與6.4.3節使用的方法相同,測試效果如圖6-20所示。
▲圖6-20 在S3C6410開發板上使用Android NDK測試word_count驅動
3.在S6410開發板上使用Java代碼測試Linux驅動
在6.4.4節編寫的測試word_count驅動的Android程序可以使用同樣的方法在S3C6410開發板上運行。測試效果與圖6-20類似。
6.4.6 將驅動編譯進Linux內核進行測試
前面幾節都是將Linux驅動編譯成模塊,然后動態裝載進行測試。動態裝載驅動模塊不會隨著Android系統的啟動而自動裝載,因此Android系統每次啟動都必須使用insmod或modprobe命令裝載Linux驅動模塊。
對于嵌入式系統(包括嵌入式Android、嵌入式Linux等)一般都采用將Linux驅動編譯進內核的方式。這樣做雖然沒有動態裝載靈活,但Linux驅動會隨著Android的啟動而自動裝載。一般在開發過程中為了測試和調試方便,會將Linux驅動以模塊形式裝載到Linux內核中。當Linux驅動通過最終測試后,會將Linux驅動編譯進Linux內核再進行測試。
本節將介紹如何將word_count驅動編譯進Linux內核,并分別在Android模擬器和S3C6410開發板上測試word_count驅動。
Linux內核源代碼被設計成可裝卸式結構。也就是說只需要修改配置文件,就可以使某個Linux驅動編譯成模塊(.ko文件),或編譯進Linux內核,當然,也可以將該Linux驅動從Linux內核去除。核心的配置文件如下。
.config:該文件位于Linux內核源代碼的頂層目錄,為隱藏文件。該文件用于配置Linux內核中的模塊。在.config文件中可以對Linux驅動進行三方面的配置:編譯成驅動模塊(.ko文件)、編譯進內核和從Linux內核去除。可以手工修改.config文件,也可以使用make menuconfig命令用菜單方式來設置.config文件。
Kconfig:每一個想要連接進Linux內核的模塊目錄都有該文件。該文件主要用于定義make menuconfig命令顯示的菜單(包括菜單項名稱、幫助信息、選項類型、模塊依賴等信息),除此之外,Kconfig文件還可以導入位于其他目錄的Kconfig文件。make命令通過Kconfig文件的遞歸引用,可以找到Linux內核中的所有Kconfig文件,從而建立一個完整的配置菜單。
Makefile:一般與Kconfig文件同時出現。每有一個Kconfig文件,就必要有一個Makefile文件。該文件用于指定如何編譯Makefile文件所在目錄的源代碼。
現在還使用word_count驅動的例子來詳細說明如何將一個Linux驅動加入Linux內核源代碼樹中。由于word_count驅動屬于字符驅動,所以可以使用如下的步驟將word_count驅動加入Linux內核源代碼樹。
第1步:將word_count.c文件放入Linux內核源代碼
將word_count.c文件放到<Linux內核目錄>/drivers/char目錄中。第2步:修改Kconfig文件打開/root/kernel/goldfish/drivers/char/Kconfig文件,找到endmenu,并在endmenu前面添加如下代碼。config WORD_COUNTbool "word_count driver" helpThis is a word count driver. It can get a word count from /dev/wordcount其中,config后面的字符串將作為Shell變量名的后半部分,前半部分是CONFIG_。也就是說,每一個具體的模塊都會對應一個Shell變量來保存該模塊的3個編譯行為(生成.ko文件、編譯進Linux內核或從Linux內核中去除)。word_count驅動模塊的變量是CONFIG_WORD_COUNT。該變量的值會保存在.config文件中。
bool表示word_count驅動只能進行兩項設置(被編譯進內核與從Linux內核中去除),后面會介紹如何設置菜單項的三項設置。bool后面的字符串就是菜單項的文本。help用于設置菜單項的幫助信息。
第3步:修改Makefile文件
打開/root/kernel/goldfish/drivers/char/Makefile文件。該文件大多都是如圖6-21所示的內容,隨便找個位置插入如下內容。
obj-$(CONFIG_WORD_COUNT) += word_count.o
通過第2步的設置產生了一個CONFIG_WORD_COUNT變量,而在第3步中obj-后使用了該變量,而不是使用固定的值(y或m)。make命令在編譯Linux內核時會將該變量替換成相應的值。
第4步:設置.config文件
.config文件可以通過手工配置,也可以通過make menuconfig命令在菜單中配置。在這里我們采用菜單配置的方法。現在進入Linux內核頂層目錄(/root/kernel/goldfish)。然后執行make menuconfig命令顯示配置菜單,并進入“Device Drivers”>“Character devices”子菜單,找到“word_count_driver”菜單項,按空格鍵將“word_count_driver”菜單項前設置成星號(*),如圖6-22所示。然后退出配置界面并保存所做的修改。
按“h”鍵可以顯示word_count驅動的幫助信息,如圖6-23所示。
在配置完.config文件后,讀者可以打開.config文件,并找到CONFIG_WORD_COUNT,會發現該變量的值已被設成“y”。
第5步:編譯Linux內核
進入/root/kernel/goldfish目錄,執行下面的命令編譯Linux內核。# make如果讀者以前編譯過當前的Linux內核,并不需要擔心編譯的時間過長,因為make足夠智能,它只會編譯最新修改的模塊及其依賴的模塊。
當成功編譯Linux內核后,讀者可以到/root/kernel/goldfish/arch/arm/boot目錄找到zImage文件,并使用Android模擬器運行這個內核。讀者會發現,在/dev目錄中有一個wordcount設備文件,而我們并沒有運行build.sh腳本文件安裝word_count驅動。這是因為Android模擬器在裝載zImage內核文件時已自動裝載了word_count驅動。不過在使用前面的例子測試word_count驅動時仍然需要執行下面的命令設置/dev/wordcount設備文件的訪問權限。
# adb shell chmod 777 /dev/wordcount如果讀者不想將word_count.c復制到/root/kernel/goldfish/drivers/char目錄,可以使用下面的命令在/root/kernel/goldfish/drivers/char目錄建立一個符號鏈接。
# ln -s /root/drivers/ch06/word_count /root/kernel/ goldfish/drivers/ char/word_ count將word_count目錄加入Linux內核源代碼樹的步驟如下(在進行下面的步驟之前需要將上面步驟所做的設置注釋掉)。
第1步:建立新的Kconfig文件
在word_count目錄中建立一個Kconfig文件,并輸入如下內容:config WORD_COUNTtristate "word_count driver" default yhelpThis is a word count driver. It can get a word count from /dev/wordcount其中tristate表示三態類型(編譯進內核、編譯成模塊,從Linux內核移除)。如果使用tristate代替bool,菜單項前面就變成尖括號。按“y”鍵,尖括號中顯示星號(*),表示編譯進內核。按“M”鍵,尖括號中顯示M,表示編譯成模塊。按“N”鍵,尖括號在符號消失,表示word_count驅動被忽略。如果不斷按“空格”鍵,這3種狀態會循環切換。
default用來設置默認值。如果使用tristate,default可以設置y、m和n三個值,分別對應編譯進內核、編譯成模塊和從Linux內核中移除。當模塊第一次設置時會處于default設置的默認狀態。
注意
如果使用tristate,必須按照節的方法打開“Enable loadable module support”選項,否則無法將驅動設為編譯成模塊狀態(M狀態),菜單項前面仍然是一對中括號。
第2步:修改Makefile文件
修改Makefile文件后,如果還想使用前面幾節的腳本文件測試word_count驅動,需要將.config文件中CONFIG_WORD_COUNT變量值設為m,如果.config文件中沒有該變量,就添加一個CONFIG_WORD_COUNT變量。當然,也可以使用make menuconfig命令設置。
為了可以單獨編譯word_count驅動,也可以和Linux內核一同編譯,我們可以采用如下形式重新編寫Makefile文件。當CONFIG_WORD_COUNT變量未定義時,說明沒有與Linux內核一同編譯。
# 與Linux內核一同編譯 ifdef CONFIG_WORD_COUNTobj-$(CONFIG_WORD_COUNT) := word_count.o else# 單獨編譯obj-m := word_count.o endif 第3步:修改上層目錄的Kconfig文件為了能找到word_count目錄中的Kconfig文件,需要在drivers/char/Kconfig文件中引用word_count目錄中的Kconfig文件。現在打開/root/kernel/goldfish/drivers/char/Kconfig文件,在“endmenu”之前添加如下一行代碼。source "drivers/char/word_count/Kconfig" 第4步:修改上層目錄的Makefile文件在drivers/char/Makefile文件中添加如下一行,以便使make命令可以找到word_count目錄中的Makefile文件。obj-$(CONFIG_WORD_COUNT) += word_count/接下來的工作就和前面介紹的五步中的第4步和第5步一樣了。在進入如圖6-24所示的設置界面時,可以按“M”鍵將word_count驅動模塊編譯成.ko文件。
當修改Linux內核設置后重新編譯內核,以前使用該Linux內核編譯的Linux驅動模塊可能由于格式錯誤無法安裝,因此,在重新編譯Linux內核后,需要重新編譯Linux驅動模塊。
如果想將word_count驅動模塊編譯進其他內核也可采用與上面類似的做法。
總結
以上是生活随笔為你收集整理的《Android深度探索(卷1):HAL与驱动开发》——6.4节使用多种方式测试Linux驱动...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《深入理解Elasticsearch(原
- 下一篇: Indicator Weather 13