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 安裝完畢。

沒有留言:

 
Blogger Templates