msleep
說明:對 msleep() 的調用是不可中斷的,能確保進程睡眠至少給定的毫秒數。
void msleep(unsigned int msecs)
{
unsigned long timeout = msecs_to_jiffies(msecs) + 1;
while (timeout)
timeout = schedule_timeout_uninterruptible(timeout);
} ##ReadMore##
參考:
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
<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。
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_dec(&count); // 將值遞減1
atomic_inc_return(&count); // 將值遞增1,並回傳目前的值(回傳整數)
atomic_inc(&count); // 將值遞增1
atomic_read(&count); // 讀取目前的值(回傳整數)
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)
For User Space(application)
#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
};
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
};
#include <signal.h>
#include <fcntl.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 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;
{
...
int fd = open("test.txt");
set_fcntl(fd);
...
return 0;
}
IPC
因為 driver 是 kernel module 的一部分,是用來提供給許許多多應用程式使用,所以函式大多定義成 reentrant,這時候如果有共享資料,就必須考慮的同步。
- 同步控制(一):Semaphore
#include <asm/semaphore.h>
static struct semaphore sem;
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--,可中斷的版本
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
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
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/ 之類的),然後需修改四個檔案:
底下簡單截取片段...
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
driver/compalec/Makefile
[註] 下 make menuconfig 去重編 kernel,可以設定要將驅動程式 build-in(*) 或者 module(M),前者就讓選項空白;後者就讓選項顯示 M。前者是開機會自動載入;後者用 lsmod 可以看到。
[註] 使用「make menuconfig」或者「make bzImage」前,須先把 gcc(build-essential)、make、以及 libncurses5-dev 安裝完畢。
假設要將 EC driver 加入,先放置到相關的資料夾中(應該是 xxx/driver/ 之類的),然後需修改四個檔案:
- driver/Kconfig
- driver/Makefile
- driver/compalec/Kconfig
- 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
# 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
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 安裝完畢。
沒有留言:
張貼留言