字元設備驅動程式
在驅動程式一開始會先讀取module_init中的devone_init初始化函式。
module_init(devone_init);
在初始話的函式中設置了,動態配置major number、設定file_oprations結構定義、自動建立對應的裝置檔。
static int devone_devs = 4; /* 這一個變數的設定它的數值影響下面會建立多少個設備 */
static int devone_init(void)
{
dev_t dev = MKDEV(devone_major, 0);
int alloc_ret = 0;
int major;
int cdev_err = 0;
struct class_device *class_dev = NULL;
#ifdef DEBUG_demo
printk("Before dynamic allocate major=%d minor=%d alloc_ret=%d\n",MAJOR(dev),MINOR(dev),alloc_ret);
#endif
alloc_ret = alloc_chrdev_region(&dev, 0, devone_devs, "devone"); /* 動態配置major number */
#ifdef DEBUG_demo
printk("After dynamic allocate major=%d minor=%d alloc_ret=%d\n",MAJOR(dev),MINOR(dev),alloc_ret);
#endif
if (alloc_ret)
goto error; /* 如果動態配置發生錯誤則跳至error */
devone_major = major = MAJOR(dev); /* 獲得major number */
#ifdef DEBUG_demo
printk("Dynamic allocate successful ! Get devone_major = major = MAJOR(dev) = %d\n",major);
#endif
/* 設定file_oprations結構定義 */
cdev_init(&devone_cdev, &devone_fops);
devone_cdev.owner = THIS_MODULE;
devone_cdev.ops = &devone_fops;
cdev_err = cdev_add(&devone_cdev, MKDEV(devone_major, devone_minor), devone_devs);
if (cdev_err)
goto error;
/* register class */
devone_class = class_create(THIS_MODULE, "devone");
if (IS_ERR(devone_class)) {
goto error;
}
/* 掛載模組時自動建立對應的裝置檔devone0 ~ devone2... */
for(devone_minor=0;devone_minor<devone_devs;devone_minor++)
{
devone_dev[devone_minor] = MKDEV(devone_major, devone_minor);
class_dev = class_device_create(
devone_class,
NULL,
devone_dev[devone_minor],
NULL,
"devone%d",
devone_minor);
}
printk(KERN_ALERT "devone driver(major %d) installed.\n", major);
return 0;
error:
if (cdev_err == 0)
cdev_del(&devone_cdev);
if (alloc_ret == 0)
unregister_chrdev_region(dev, devone_devs);
return -1;
}
File_operations結構定義,其中設定了open、release、write、read…等
struct file_operations devone_fops = {
.open = devone_open,
.release = devone_close,
.write = devone_write,
.read = devone_read,
};
在此file_oprations中還設定了Handler切換
static int devone_open(struct inode *inode, struct file *file)
{
printk("%s: major %d minor %d (pid %d)\n", __func__,
imajor(inode),
iminor(inode),
current->pid
);
/* 透過minor number去做切換Handler選擇 */
switch (iminor(inode)) {
case 0:
file->f_op = &zero_fops;
break;
case 1:
file->f_op = &one_fops;
break;
default:
return 0;
}
/* 設定當檔案有開啟時,才實行上面所做過的選擇。 */
if (file->f_op && file->f_op->open)
return file->f_op->open(inode, file);
return 0;
}
另外所設定的file_opreations也跟上面一樣都有open、release、write、read。
struct file_operations zero_fops = {
.open = zero_open,
.release = zero_close,
.read = zero_read,
.write = zero_write,
};
struct file_operations one_fops = {
.open = one_open,
.release = one_close,
.read = one_read,
.write = one_write,
};
當卸載sample模組時,會先讀取module_exit中devone_exit函式。
module_exit(devone_exit);
在裡面做,刪除裝置、刪除class、及註銷之前註冊的裝置。
static void devone_exit(void)
{
dev_t dev = MKDEV(devone_major, 0);
int i;
/* unregister class */
for(i=0;i<devone_devs;i++)
{
class_device_destroy(devone_class, devone_dev[i]); /* 刪除裝置 */
class_destroy(devone_class); /* 刪除先前登記的 class */
}
cdev_del(&devone_cdev);
unregister_chrdev_region(dev, devone_devs);
printk(KERN_ALERT "devone driver removed.\n");
}
實際測試
當插入移除模組時,可以在下面這裏看到之前設定printfk()顯示設定的資訊。
# insmod sample.ko
# rmmod sample
# dmesg | tail
[47692.501184] Before dynamic allocate major=0 minor=0 alloc_ret=0
[47692.501397] After dynamic allocate major=253 minor=0 alloc_ret=0
[47692.501416] Dynamic allocate successful ! Get devone_major = major = MAJOR(dev) = 253
[47692.502622] devone driver(major 253) installed.
[47698.980427] devone driver removed.
執行下面這行指令,可以看到自動建立的設備
# ls /dev/devone*
/dev/devone0 /dev/devone1 /dev/devone2 /dev/devone3
編譯 ;執行simple.c範例
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define DEVFILE "/dev/devone0"
int open_file(void)
{
int fd;
printf("Start open fd\n");
fd = open(DEVFILE, O_RDWR);
printf("Open fd finish\n");
if (fd == -1) {
printf("open event\n");
perror("open");
}
return (fd);
}
void close_file(int fd)
{
if (close(fd) != 0) {
printf("close event\n");
perror("close");
}
}
int main(void)
{
int fd;
printf("Call open function\n");
fd = open_file();
printf("fd :%d\n",fd);
sleep(1);
printf("Call close function\n");
close_file(fd);
return 0;
}
由於已經自動建立了設備,所以執行完這個程式之後出現了下面的結果,可以看出驅動程式在讀取設備時是沒問題的。
結果:
Call open function
Start open fd
Open fd finish
fd :3
Call close function
也可以利用cat來檢視設備。
# cat /dev/devone0
# cat /dev/devone1
# cat /dev/devone2
# dmesg | tail
[48378.554835] devone_open: major 253 minor 0 (pid 18368)
[48378.554845] zero_open: major 253 minor 0 (pid 18368)
[48378.554987] zero_read called
[48378.555009] zero_close: major 253 minor 0 (pid 18368)
[48381.676440] devone_open: major 253 minor 1 (pid 18369)
[48381.676541] one_open: major 253 minor 1 (pid 18369)
[48381.676602] one_read called
[48381.676681] one_close: major 253 minor 1 (pid 18369)
[48384.888112] devone_open: major 253 minor 2 (pid 18370)
[48384.889202] devone_read called
[48384.889618] devone_close: major 253 minor 2 (pid 18370)
資料來源:
博碩 Linux Device Driver Programming
設備驅動程式講義 by 明新科技大學 曾宏立 老師