Pages

2011年12月28日 星期三

Linux Driver Build & Common Struct/API

msleep

說明:對 msleep() 的調用是不可中斷的,能確保進程睡眠至少給定的毫秒數。
 

     void msleep(unsigned int msecs)
     {
         unsigned long timeout = msecs_to_jiffies(msecs) + 1;
         while (timeout)
             timeout = schedule_timeout_uninterruptible(timeout);
     } ##ReadMore##

參考



dmesg
  • 在 kernel module 中 printk,可以透過 dmesg 印出訊息,至於訊息的內容,<x> 是優先權[xxxx.xxxxxx] 是時間,單位是秒
<4>[ 8544.290000] 14: get interrupt
<4>[ 8544.290000] [x,y]=(583,259)14: input_sync
<4>[ 8544.320000] [x,y]=(583,259)14: input_sync



Device Node

  • MKDEV (引入標頭檔 #include <linux/cdev.h>)mudule_init 的 callback function 中加入
    -----------------------------------------------------
    struct cdev * Videocdev;
    int ret;
    dev_t devNum;
    devNum = MKDEV(88, 0);
    ret = register_chrdev_region(devNum, 2, "/dev/video1" );
    Videocdev = cdev_alloc(); // struct cdev *cdev_alloc(void);
    cdev_add( Videocdev , devNum , 1 ); // int cdev_add(struct cdev *, dev_t, unsigned);

    Videocdev->owner = THIS_MODULE; // #define THIS_MODULE (&__this_module)
    Videocdev->ops = &uvc_fops;
    mudule_exit 的 callback function 中加入
    -----------------------------------------------------
    cdev_del(Videocdev); // void cdev_del(struct cdev *);
    unregister_chrdev_region(MKDEV(88, 0) , 2); // extern void unregister_chrdev_region(dev_t, unsigned);
    另外在程式中用 mknod 建立裝置節點。


Copy Function

  • copy_to_user
    當應用程式利用 read 讀取某裝置的資料,例如 camera,整個呼叫過程是:read -> ... -> sys_read -> ... -> read file operation,然後 uvcvideo driver 裡頭實作 read file operation,就會透過 copy_to_user,把資料複製給應用程式。
  • copy_from_user
    當應用程式利用 write 寫入資料到某裝置,就會呼叫這支 API。

Task Struct

  • task_struct這個結構定義在 #include/linux/sched.h,他就是我們熟知的 Process Control Block,他紀錄著一個行程的一大堆資訊,參考網址。如果我們想要知道目前取得 cpu 執行的程式名稱、process id 等等,可以透過巨集 current 來得到。
char process_name[] = current->comm; // 目前執行的應用程式名稱
pid_t process_id = current->tgid; // process id (thread group id)
pid_t thread_id = current->pid // thread id

[註] 若沒有使用執行緒,則 pid = tgid。


Atomic Operation

  • 連動運算(Atomic Operation)
    我們平常做的 ++a 其實是兩個動作:加法與指派。為了確保這兩個指令連續執行,所衍生的產物。若沒有這種東西,而程式開發者也未對資料做同步,就會因為 race condition 造成資料上的問題。

#include <asm/atomic.h> // typedef struct { int counter; } atomic_t;

atomic_t count = ATOMIC_INIT(0);

atomic_dec(&
count); // 將值遞減1

atomic_inc_return(&
count); // 將值遞增1,並回傳目前的值(回傳整數)

atomic_inc(&
count); // 將值遞增1

atomic_read(&
count);
// 讀取目前的值(回傳整數)

...


Asynchronous Notification

  • 異步通知(Asynchronous Notification)
    這是驅動程式用來發送 signal 給應用程式的方式,而發送的訊號類型為 SIGIO原始碼
For Kernel Space(device driver)

#include <linux/fs.h>

static struct fasync_struct async_queue; // 資料結構

kill_fasync(&async_queue, SIGIO, POLL_IN); // 發送通知給 file owner

static int uvc_v4l2_fasync(int fd, struct file* filp, int mode)
{
     int ret = fasync_helper(fd, filp, mode, &async_queue); // 加入異步通知的名單
     return ret;
}

uvc_v4l2_fasync(-1, file, 0); // 從異步名單中移除

struct file_operations uvc_fops = {
     .owner = THIS_MODULE,
     .open = uvc_v4l2_open,
     .release = uvc_v4l2_release,
     ....
     .fasync = uvc_v4l2_fasync, // 新增 file operation
};

For User Space(application)

#include <signal.h>
#include <fcntl.h>

// 利用 F_SETFL 請求加入通知名單,這會對應到 .fasync operation
void set_fcntl(int fd) 
{
     int ori_flags;
     fcntl(fd, F_SETOWN, getpid());
     ori_flags = fcntl(fd, F_GETFL);
     fcntl(fd, F_SETFL, ori_flags|FASYNC); // 加入通知名單
}

int main(int argc, char* argv[])
{
     ...
     int fd = open("test.txt");
    
set_fcntl(fd);
     ...
     return 0;
 
}



IPC

因為 driver 是 kernel module 的一部分,是用來提供給許許多多應用程式使用,所以函式大多定義成 reentrant,這時候如果有共享資料,就必須考慮的同步。
  • 同步控制(一):Semaphore
#include <asm/semaphore.h>
static struct semaphore sem;


sema_init(&sem, 1); // 可自行設定初值

init_MUTEX_LOCKED(&sem); // 初值為0
init_MUTEX_UNLOCKED(&sem);
// 初值為1

if(down_interruptible(&sem)) return -ERESTARTSYS; // sem--,可中斷的版本

Critical Section
up(&sem);
  • 同步控制(二):Mutex
#include <linux/mutex.h>
struct mutex mymutex;

mutex_init(&mymutex);
// 設定初值,且值為1



mutex_lock(&mymutex);
Critical Section
mutex_unlock(&mymutex);

不論使用何種同步控制,一定要把值恢復,否則一堆行程都會被卡死,造成系統當機。另外還有一種叫作「spinlock」,和上述兩種的差異是,spinlock 是 busy-waiting 的,所以,該同步控制的 Critical Section 絕對不能休眠(ready->sleep)。
  • 同步控制(三):Spinlock
#include <linux/spinlock.h>

static spinlock_t rw_lock;

spin_lock_init(&rw_lock);

spin_lock_irqsave(&rw_lock, irq_flags);
spin_unlock_irqrestore(&rw_lock, irq_flags);

spin_lock(&rw_lock);
spin_unlock(&rw_lock);


參考資料:http://www.lslnet.com/linux/f/docs1/i34/big5261423.htm



Build Kernel/Driver

驅動程式編譯的方式有兩種,一種是透過 /usr/src/ 底下的 kernel source tree(kernel header);另一種就是直接和 kernel source code 一起編譯。以下要說明若使用後者時,應該如何設定 Kconfig 和 Makefile:

假設要將 EC driver 加入,先放置到相關的資料夾中(應該是 xxx/driver/ 之類的),然後需修改四個檔案:
  1. driver/Kconfig 
  2. driver/Makefile
  3. driver/compalec/Kconfig 
  4. driver/compalec/Makefile

底下簡單截取片段...

driver/Kconfig
# drivers/Kconfig
menu "Device Drivers"
...
source "drivers/auxdisplay/Kconfig"
source "drivers/uio/Kconfig"
source "drivers/android/Kconfig"
source "drivers/uvcvideo/Kconfig"
source "drivers/compalec/Kconfig"
endmenu

driver/Makefile
obj-y += android/
obj-$(CONFIG_SSB) += ssb/
obj-$(CONFIG_VIRTIO) += virtio/

obj-$(CONFIG_UVCVIDEO) += uvcvideo/
obj-$(CONFIG_COMPAL_EC) += compalec/

driver/compalec/Kconfig
#
# Block device driver configuration
#
menuconfig COMPAL_EC_DRIVER
bool "Compal Ec"
default y
---help---
Say Y here to get to see options for Compal Ec driver.
If you say N, all options in this submenu will be skipped and disabled;
only do this if you know what you are doing.
if COMPAL_EC_DRIVER # if choosing menuconfig item, keep going config COMPAL_EC
tristate "Compal Ec driver"
---help---
This is the Compal Ec driver. Say Y or M.
endif # COMPAL_EC_DRIVER

driver/compalec/Makefile
EcIO-objs := EcIOMain.o EcLowerApi.o EcIndexMode.o EcPortCtrl.o Ec686C.o
obj-$(CONFIG_COMPAL_EC) += EcIO.o

[註] 下 make menuconfig 去重編 kernel,可以設定要將驅動程式 build-in(*) 或者 module(M),前者就讓選項空白;後者就讓選項顯示 M。
前者是開機會自動載入;後者用 lsmod 可以看到。


[註] 使用「make menuconfig」或者「make bzImage」前,須先把 gcc(build-essential)、make、以及 libncurses5-dev 安裝完畢。

2011年11月15日 星期二

[Cupcake] Multi-touch Part2

以下是在 Androdi 1.5 Platform 實現,當初的 MotionEvent 並沒有 Multi-Touch 相關欄位,所以是拿 size(4 bytes) 欄 來替代,現在 2.x 版已經有這些欄位。

[Cupcake] Multi-touch Part1

  由於 Android 1.5 還沒支援多點觸控, 對於我們這種看人臉色吃飯的系統廠, 一種方式就是坐以待斃, "要等 Google 之後的版本支援才能做, 目前辦不到"。但 touch panel 明明可以支援到兩點觸控, 所以, 秉持身為 RD 的堅持, 在稍微了解相關的程式碼後, 我們只要稍微修改一下 driver 和 package, 就讓 Android 1.5 的應用程式也可以做到 multi-touch, 底下這張圖顯示底層收到的封包隔式、driver/framework在單指以及雙指的數值表現:

AnalogClock - Matrix Rotation

旋轉
         這個範例直接對 Canvas 做旋轉,然後把圖(Drawable)畫上去,就可以達到圖片旋轉的效果。##ReadMore##

廣播
          AnalogClock 在類別中宣告一個 BroadcastReceiver 變數,並實作 onReceive,而 IntentFilter 的描述直接在程式碼中,非 AndroidManifest.xml。
IntentFilter filter = new IntentFilter(); 
filter.addAction(Intent.ACTION_TIME_TICK); 
filter.addAction(Intent.ACTION_TIME_CHANGED); 
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 
getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);

參考資料

Calculate Time Consuming

Java Layer
  • Timer
    • System.nanoTime .. (Link)
    • SystemClock.uptimeMillis .. (Link)
    • SystemClock.elapsedRealtime .. (Link)
  • System Time (?)
    • Data Class .. (Link
##ReadMore##
C++ Layer
(#include <utils/Timers.h>)
  • Timer
    • DurationTimer
      • DurationTimer::start()
      • DurationTimer::stop()
      • DurationTimer::durationUsecs

[Froyo] Compatibility Test Suite

       Android 是一個 Open Source Project,每個人都可以自由修改,也因此市面上有許多 Andorid Device,Google 為了確保 Android 程式可以順利在這些裝置上運行,提供 CTS 這個工具。這在 Android 2.1 版之前是無法自由取得,不過從這個版本開始,可以從這個網頁取得;另外還有簡介。##ReadMore##甚至在 Android Source Code 都可以從 source root 看到 cts/ 這個資料夾,不過我編譯後,雖然成功在 ASOP/out/host/bin 產生 cts 的腳本,但是相關的xml 與 apk 都沒有在 ASOP/out/host/linux-x86/cts 產生。另外,還要下載 SDK,因為會用到 adb 這個工具。

使用方式暫時參考底下的連結,因為還沒有裝置可以親自測試。已經在 NAZ10 上面測試,目前還在跑,我是下載 2.1 版的 sdk 與 cts,然後記得設定 SDK_ROOT,因為會用到 adb,然後執行方式,例如:
  1. 啟動CTS:./startcts
  2. 列舉有多少plan可以跑:ls --plan (擺在android-cts/repository/plans)
  3. 執行某個plan:start --plan VM   (執行的對象在android-cts/repository/testcases)
        其他用法,就參考底下連結吧!執行後的結果會放在 android-cts/repository/results,但我沒去研究 result 都寫些甚麼。

        保險起見,須先確認 SDK 版本,因為就算是給 2.2 是用,r6 或者 r7 跑出來的相容性測試結果就可能不同。至於要如何判斷正確的 SDK 版本,目前想到的方式是,直接使用 Android source code 編譯出來的 CTS 與 SDK tools,然後去執行它,就會出現 SDK 版本資訊,只是說,可能需要處理一下擺放路徑所造成的錯誤訊息。

參考資料:

Intent Sample

        在同一個 package 中,利用 Intent 啟動另一個 Activity,並透過 Bundle 物件帶參數給他 (下面的例子是送一個字串給接收端的 TextView) ##ReadMore##

<<發送端>>
Intent intent = new Intent();
intent.setClass(this, IntentReceiving.class);

Bundle bundle = new Bundle();
bundle.putString("Receive", "Intent Receiving");  // (Key - Value)
intent.putExtras(bundle);      

startActivity(intent);

<<接收端>>
CharSequence cs;
Bundle bundle = this.getIntent().getExtras();
cs = bundle.getString("Receive");
textview.setText(cs);

        也可以不透過 Bundle,直接 intent.setString() 或 intent.setInt()。另外,在宣告 IntentFilter 時,除了在 AndroidManifest.xml 中定義;也可直接在程式碼中描述。

參考資料

System Resource

        在 framework/base/core/res 有許多已經定義好的資源可以使用,但自己寫得應用程式,通常還是在自己的 res/。##ReadMore##

        假設現在我要讓 Settings->Display->Brightness 的 Preference Dialog 出現 Auto Brightness 的 checkbox 和 text。只要在 framework/base/core/res/res/values/config.xml 做修改:
<bool name="config_automatic_brightness_available">false</bool>

        這樣一來,這個 checkbox 和 text 就跑出來了。至於說,為甚麼這樣就會出現,是因為在 BrightnessPreference.java 在建構函式讀取這個變數值,來決定這個 UI 要不要秀出來。
mAutomaticAvailable = context.getResources().getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);

Thread & Handler

   如果在某個 Activity/Service/Class 中使用執行緒,然後,需透過該執行緒來變更/通知某個事件,就可以使用 Handler。##ReadMore##
private static final int LIGHTSENSOR_LUX = 0x101;   // 自訂常數

Thread t = new Thread() {
    public void run() {
        while (true) {
            int getLuxValue = 0;

            Message msg = new Message();
            msg.what = LIGHTSENSOR_LUX;
            msg.arg1 = getLuxValue;
            handler.sendMessage(msg); 

            try {   
               Thread.sleep(500);    
            } catch (InterruptedException e) {   
               Thread.currentThread().interrupt();   
            }
        }
    }
};
t.start();

private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {   
        case LIGHTSENSOR_LUX:
            Log.e("Test", "Lux Value = "+msg.arg1);
            break; 
    }   
    super.handleMessage(msg);
}

   Message 提供三個公開變數可以傳資料:arg1、arg2 是傳整數,obj 是傳 object type 資料;還有 setData(Bundle) 可以使用,但還沒用過。

參考網址

  • 利用 Handler 來更新 Android UI .. (Link)
  • Andorid Developer Messge .. (Link)
  • Andorid Developer Handler .. (Link)
  • Why isn't the thread stopping .. (Link)
  • 背景執行服務(Service) .. (Link)

2011年11月10日 星期四

[Froyo] Airplane Mode

        Airplane Mode 開啟就會斷掉所有通訊 (BT、WiFi、3G)。在 Android 上,這個功能要能夠正常運作,必須有 3G module。過程如下:

 
Blogger Templates