《Linux驱动》2--字符设备驱动

一、字符设备驱动简介

        字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节
流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的点灯、按键、 IIC、 SPI,
LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。
        Linux应用程序对驱动程序的调用:

        Linux 中一切皆为文件,驱动加载成功以后会在“/dev”目录下生成一个相应的文件,应用程序通过对这个名为“/dev/xxx” (xxx 是具体的驱动文件名字)的文件进行相应的操作即可实现对硬件的操作。open/release是打开/关闭设备文件,read/write是读/写数据。

        应用程序运行在用户空间,而 Linux 驱动属于内核的一部分,因此驱动运行于内核空间。当我们在用户空间想要实现对内核的操作,比如使用 open 函数打开/dev/led 这个驱动,因为用户空间不能直接对内核进行操作,因此必须使用一个叫做“系统调用”的方法来实现从用户空间“陷入” 到内核空间,这样才能实现对底层驱动的操作。 open、 close、 write 和 read 等这些函数是由 C 库提供的,在 Linux 系统中,系统调用作为 C 库的一部分。当我们调用 open 函数的时候流程如图 所示:

        其中关于 C 库以及如何通过系统调用“陷入” 到内核空间这个我们不用去管,我们重点关注的是应用程序和具体的驱动,应用程序使用到的函数在具体驱动程序中都有与之对应的函数,比如应用程序中调用了 open 这个函数,那么在驱动程序中也得有一个名为 open 的函数。每一个系统调用,在驱动中都有与之对应的一个驱动函数,在 Linux 内核文件 include/linux/fs.h 中有个叫做file_operations 的结构体,此结构体就是 Linux 内核驱动操作函数集合,通过函数指针的方式实现面向对象,内容如下所示:

        在字符设备驱动开发中最主要的工作就是实现上面这些函数,不一定全部都要实现,但是像 open、 release、 write、 read 等都是需要实现的,具体需要实现哪些函数还是要看具体的驱动要求。

二、字符驱动开发的步骤

        2.1驱动模块的加载和卸载

module_init(xxx_init); //注册模块加载函数
module_exit(xxx_exit); //注册模块卸载函数

        module_init 函数用来向 Linux 内核注册一个模块加载函数,参数 xxx_init 就是需要注册的具体函数,当使用“insmod”命令加载驱动的时候, xxx_init 这个函数就会被调用。 module_exit()函数用来向 Linux 内核注册一个模块卸载函数,参数 xxx_exit 就是需要注册的具体函数,当使用“rmmod”命令卸载具体驱动的时候 xxx_exit 函数就会被调用。

        编译完成后可以用insmod加载模块,也可以用rmmod卸载模块。

        2.2字符设备注册与注销

        对于字符设备驱动而言,当加载驱动模块时还需要注册字符设备,同样,卸载驱动模
块的时候也需要注销掉字符设备。字符设备的注册和注销函数原型如下所示:

static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
static inline void unregister_chrdev(unsigned int major, const char *name)

register_chrdev函数用于注册字符设备,此函数一共有三个参数,这三个参数的含义如下:
        major: 主设备号, Linux 下每个设备都有一个设备号,可以用cat /proc/devices查看已有的设备号,不可以使用已有的设备号。
        name:设备名字,指向一串字符串。
        fops: 结构体 file_operations 类型指针,指向设备的操作函数集合变量。
unregister_chrdev 函数用户注销字符设备,此函数有两个参数,这两个参数含义如下:
        major: 要注销的设备对应的主设备号。
        name: 要注销的设备对应的设备名。

        所以在xxx_init和xxx_exit中,可以需要调用注册/注销函数。

static int __init hello_init(void)
{
	int ret = 0;
	ret	= register_chrdev(200, "hello", &hello_fops);
	if(ret < 0)
	{
		printk("register chardev fail.\n");
		return -1;
	}
	else
	{	
		printk("register chardev success.\n");
		return 0;
	}
}


static void __exit hello_exit(void)
{
	unregister_chrdev(200, "hello");
	printk("hello exit!\r\n");
}

         里面使用到了一个struct file_operations的结构体指针,还需要实现这个这个结构体里的函数。这里的函数都先空实现。

static int hello_open(struct inode *inode, struct file *filp)
{
	return 0;
}

static ssize_t hello_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static int hello_release(struct inode *inode, struct file *filp)
{
	return 0;
}


static struct file_operations hello_fops = {
	.owner   = THIS_MODULE,	
	.open    = hello_open,
	.read    = hello_read,
	.write   = hello_write,
	.release = hello_release,
};

2.3添加LICENSE和作者信息

        最后我们需要在驱动中加入 LICENSE 信息和作者信息,其中 LICENSE 是必须添加的,否
则的话编译的时候会报错,作者信息可以添加也可以不添加。 LICENSE 和作者信息的添加使用
如下两个函数:

MODULE_LICENSE() //添加模块 LICENSE 信息
MODULE_AUTHOR() //添加模块作者信息

2.4最终模板

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>

static int hello_open(struct inode *inode, struct file *filp)
{
	
	return 0;
}

static ssize_t hello_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static ssize_t hello_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}

static int hello_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static struct file_operations hello_fops = {
	.owner   = THIS_MODULE,	
	.open    = hello_open,
	.read    = hello_read,
	.write   = hello_write,
	.release = hello_release,
};

static int __init hello_init(void)
{
	int ret = 0;
	ret	= register_chrdev(200, "hello", &hello_fops);
	if(ret < 0)
	{
		printk("register chardev fail.\n");
		return -1;
	}
	else
	{	
		printk("register chardev success.\n");
		return 0;
	}
}


static void __exit hello_exit(void)
{
	unregister_chrdev(200, "hello");
	printk("hello exit!\r\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值