字符设备驱动程序源码
#include
#include
#include
#include
/*定义一个字符设备对象*/
static struct cdev mycdev;
/*字符设备节点的设备号*/
static dev_t ndev;
static int mycdev_open(struct inode *nd, struct file *filp)
{
int major = MAJOR(nd->i_rdev);
int minor = MINOR(nd->i_rdev);
printk("mycdev_open, major=%d, minor=%d\n", major, minor);
return 0;
}
static ssize_t mycdev_read(struct file *f, char __user * u, size_t sz, loff_t * off)
{
printk("In the mycdev_read() function!\n");
return 0;
}
static int mycdev_release(struct inode *nd, struct file *filp)
{
printk("In the mycdev_release() function!\n");
return 0;
}
/*字符设备驱动程序中非常关键的一个数据结构 struct file_operations*/
struct file_operations chr_ops = {
.owner = THIS_MODULE,
.open = mycdev_open,
.read = mycdev_read,
.release = mycdev_release,
};
/*模块的初始化函数*/
static int mycdev_init(void)
{
int ret = 0;
/*初始化字符设备对象 */
cdev_init(&mycdev, &chr_ops);
/*分配设备号 */
ret = alloc_chrdev_region(&ndev, 0, 1, "mycdev");
if (ret < 0)
return ret;
printk("mycdev_init():major=%d, minor=%d\n", MAJOR(ndev), MINOR(ndev));
/*将字符设备对象 mycdev 注册进系统 */
ret = cdev_add(&mycdev, ndev, 1);
if (ret < 0)
unregister_chrdev_region(ndev, 1);
return ret;
}
static void mycdev_exit(void)
{
printk("Removing mycdev module...\n");
/*将字符设备对象 mycdev 从系统中卸载掉 */
cdev_del(&mycdev);
/*释放分配的设备号 */
unregister_chrdev_region(ndev, 1);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bilaizi");
MODULE_DESCRIPTION("A char device driver as an example");
可以参照下面这个简单的 Makefile 文件来编译上述的模块:
# Makefile2.6
obj-m :=mycdev.o # 产生mycdevmod 模块的目标文件
CURRENT_PATH := $(shell pwd) #模块所在的当前路径
LINUX_KERNEL := $(shell uname -r) #Linux内核源代码的当前版本
LINUX_KERNEL_PATH := /usr/src/kernels/$(LINUX_KERNEL)/ #Linux内核源代码的绝对路径
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules #编译模块
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean #清理
应用程序源码mycdevtest.c
#include
#include
#include
#define CHR_DEV_NAME "/dev/mycdev"
int main()
{
int ret;
char buf[32];
int fd = open(CHR_DEV_NAME, O_RDONLY | O_NDELAY);
if (fd < 0) {
printf("open file %s failed!\n", CHR_DEV_NAME);
return -1;
}
read(fd, buf, 32);
close(fd);
return 0;
}
可以用 gcc 来生成该应用程序的可执行文件 mycdevtest:
[root@bilaizi 桌面]# gcc -o mycdevtest mycdevtest.c
具体操作步骤如下:
[root@bilaizi 桌面]# insmod mycdev.ko
[root@bilaizi 桌面]# dmesg
mycdev_init():major=247, minor=0
通过上面 dmesg 的输出信息,我们知道 alloc_chrdev_region 函数给内核模块 mycdev.ko
分配的主设备号为 247,次设备号为 0。根据这个设备号信息用 mknod 命令在系统的/dev 目录下为
该模块生成一个新的设备文件节点:
[root@bilaizi 桌面]#mknod /dev/mycdev c 247 0
如果一切正常,那么在/dev 目录下就会产生一个新的设备文件节点“/dev/mycdev”可以用 ls 命令来仔细观察一下它:
[root@bilaizi 桌面]# ls -l /dev/mycdev
crw-r--r--. 1 root root 247, 0 11月 2 20:38 /dev/mycdev
上面 ls 命令的输出反映出了设备节点“/dev/chr_dev”的如下一些关键信息:
“crw-r--r--”中的字符“c”表明这是个字符设备文件,247 是该设备节点的主设备号,次设备号则
是 0,这跟我们的预期是完全一致的。
有了对应的设备文件之后,现在可以运行我们的应用程序了:
[root@bilaizi 桌面]#./mycdev
[root@bilaizi 桌面]#dmesg -c
mycdev_open, major=247, minor=0
In the mycdev_read() function!
In the mycdev_release() function!
对比前面内核模块 mycdev.ko 的源码,读者应该知道上述两行的输出分别来自内核模块中的
mycdev_open、mycdev_read 和mycdev_release 函数,虽然在这个示例程序中它们几乎没做任何事情,但是我
们见证了应用程序成功调用到了设备驱动程序实现的函数,这正是我们所预期的目标。
阅读(1140) | 评论(1) | 转发(0) |