轉自:http://blog.csdn.net/racehorse/archive/2007/11/14/1884038.aspx
看了《Linux裝置驅動程式》的前幾章,我結合這篇教程中給出的一個2.4版核心的字元驅動,自己編寫了一個2.6版核心的驅動程式,並且加上了詳細的注釋。這個程式很簡單,但是對初學者把握2.6版核心的字元驅動的脈絡應該有一定的協助,也可以算作我對《Linux裝置驅動程式》前幾章學習的一個小結。
#globalvar.c
#include <linux/module.h> //模組所需的大量符號和函數定義
#include <linux/init.h> //指定初始化和清楚函數
#include <linux/fs.h> //檔案系統相關的函數和標頭檔
#include <linux/cdev.h> //cdev結構的標頭檔
#include <asm/uaccess.h> //在核心和使用者空間中移動資料的函數
MODULE_LICENSE("GPL"); //指定代碼使用的許可證
//檔案操作函數的聲明
int globalvar_open(struct inode *, struct file *);
int globalvar_release(struct inode *, struct file *);
ssize_t globalvar_read(struct file *, char *, size_t, loff_t *);
ssize_t globalvar_write(struct file *, const char *, size_t, loff_t *);
int dev_major = 50; //指定主裝置號
int dev_minor = 0; //指定次裝置號
struct file_operations globalvar_fops= //將檔案操作與分配的裝置號相連
{
owner: THIS_MODULE, //指向擁有該模組結構的指標
open: globalvar_open,
release: globalvar_release,
read: globalvar_read,
write: globalvar_write,
};
struct globalvar_dev //用來表示我們定義裝置的結構
{
int global_var; //這個變數代表要操作的裝置
struct cdev cdev; //核心中表示字元裝置的結構
};
struct globalvar_dev *my_dev; //裝置結構的指標
static void __exit globalvar_exit(void) //結束模組時的操作
{
dev_t devno=MKDEV(dev_major, dev_minor); //dev_t是用來表示裝置編號的結構
cdev_del(&my_dev->cdev); //從系統中移除一個字元裝置
kfree(my_dev); //釋放自訂的裝置結構
unregister_chrdev_region(devno, 1); //登出登入的驅動程式
printk("globalvar unregister success\n");
}
static int __init globalvar_init(void) //初始化模組的操作
{
int ret, err;
dev_t devno=MKDEV(dev_major, dev_minor);
//動態分配裝置號,次裝置號已經指定
ret=alloc_chrdev_region(&devno, dev_minor, 1, "globalvar");
//儲存動態分配的主裝置號
dev_major=MAJOR(devno);
//根據期望值分配裝置號
//ret=register_chrdev_region(devno, 1, "globalvar");
if(ret<0)
{
printk("globalvar register failure\n");
globalvar_exit(); //如果註冊裝置號失敗就退出系統
return ret;
}
else
{
printk("globalvar register success\n");
}
//為裝置在核心空間分配空間
my_dev=kmalloc(sizeof(struct globalvar_dev), GFP_KERNEL);
if(!my_dev)
{
ret=-ENOMEM; //如果分配失敗返回錯誤資訊
printk("create device failed\n");
}
else //如果分配成功就可以完成裝置的初始化
{
my_dev->global_var=0; //裝置變數初始化為0
cdev_init(&my_dev->cdev, &globalvar_fops); //初始化裝置中的cdev結構
my_dev->cdev.owner=THIS_MODULE; //初始化cdev中的所有者欄位
err=cdev_add(&my_dev->cdev, devno, 1); //向核心添加這個cdev結構的資訊
if(err<0)
printk("add device failure\n"); //如果添加失敗列印錯誤訊息
}
return ret;
}
//開啟裝置檔案系統調用對應的操作
int globalvar_open(struct inode *inode, struct file *filp)
{
struct globalvar_dev *dev;
//根據inode結構的cdev欄位,獲得整個裝置結構的指標
dev=container_of(inode->i_cdev, struct globalvar_dev, cdev);
//將file結構中的private_data欄位指向已指派的裝置結構
filp->private_data=dev;
return 0;
}
//關閉裝置檔案系統調用對應的操作
int globalvar_release(struct inode *inode, struct file *filp)
{
return 0;
}
//讀裝置檔案系統調用對應的操作
ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
//擷取指向已指派資料的指標
struct globalvar_dev *dev=filp->private_data;
//將裝置變數值複製到使用者空間
if(copy_to_user(buf, &dev->global_var, sizeof(int)))
{
return -EFAULT;
}
return sizeof(int); //返回讀取資料的大小
}
//寫裝置檔案系統調用對應的操作
ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)
{
//擷取指向已指派資料的指標
struct globalvar_dev *dev=filp->private_data;
//從使用者空間複製資料到核心中的裝置變數
if(copy_from_user(&dev->global_var, buf, sizeof(int)))
{
return -EFAULT;
}
return sizeof(int); //返回寫資料的大小
}
module_init(globalvar_init); //模組被裝載時調用globalvar_init
module_exit(globalvar_exit); //模組被卸載時調用globalvar_exit
按如下內容編寫一個Makefile檔案,然後輸入make就可以開始自動編譯了。編譯之後得到了一個名為globalvar.ko的模組檔案,這就是我們需要的裝置驅動檔案。
#Makefile
ifneq ($(KERNELRELEASE), )
obj-m := globalvar.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
endif
接下來運行如下代碼,將驅動加入核心。
insmod globalvar.ko
此時可以用dmesg或lsmod命令檢查一下模組載入是否成功。如果沒有問題,就可以使用mknod構造一個裝置檔案:
mknod /dev/globalvar c
最後,按照下面內容編寫一個簡單的測試檔案並用gcc編譯。
#test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
main()
{
int fd, num;
fd=open("/dev/globalvar", O_RDWR, S_IRUSR|S_IWUSR); //可讀寫方式開啟裝置檔案
if(fd!=-1)
{
read(fd, &num, sizeof(int)); //讀取裝置變數
printf("The globalvar is %d\n", num);
printf("Please input the num written to globalvar\n");
scanf("%d", &num);
write(fd, &num, sizeof(int)); //寫裝置變數
read(fd, &num, sizeof(int)); //再次讀取剛才寫的值
printf("The globalvar is %d\n", num);
close(fd); //關閉裝置檔案
}
else
{
printf("Device open failure\n");
}
}
如果前面的內容都無誤,就可以使用上面的測試程式讀寫剛才編寫的裝置檔案了。