字符设备驱动加载函数
有了《内核模块编写及编译》和 《字符设备驱动(1)》的基础,并且字符设备驱动对上注册给内核,对下要操作硬件,所以分成2层:
//加载函数(必须)
static int hello_init(void)//返回值是int类型,函数名自定义,参数是void
{
printk(KERN_ALERT "Hello World.\n");
//up kernel
//down hardware
return 0;
}
备注:上面只写了加载函数。
其中对上注册给内核,又分成3步:1、注册设备号,2、初始化字符设备结构体,3、添加字符设备结构体给内核。类似去体检,先要预约个体检号(第一步),去之前要准备一下,比如前一天饮食清淡点,早上空腹等等(第二步),最后在去体检(第三步)。
这三步分别有3个函数对应:
#include <linux/fs.h>
//1、注册设备号,下面2个二选一
//1.1、提前规划好要用的设备号,向内核单独申请
int register_chrdev_region(dev_t from, unsigned count, const char *name)
//或者
//1.2、不知道使用哪些设备号,向内核动态申请
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
#include <linux/cdev.h>
//2、初始化字符设备结构体
void cdev_init(struct cdev *cdev , const struct file_operations *fops)
//3、添加字符设备结构体给内核
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
所以字符设备驱动加载函数如下:
//需要是全局变量
struct cdev cdevice;
struct file_operations cdev_ops;
//加载
static int hello_init(void)
{
dev_t devno = MKDEV(MAJOR_CHAR,MINOR_CHAR);
int ret = -1;
//up kernel
//1、注册设备号
ret = register_chrdev_region(devno, 1, "hello");
if (0 != ret)
{
printk("Register char device failed.\n");
return ret;
}
printk("Register char device ok.\n");
//2、初始化字符设备结构体
cdev_init(&cdevice, &cdev_ops);
cdevice.owner = THIS_MODULE;
//3、添加字符设备结构体给内核
ret = cdev_add(&cdevice,devno , 1);
if (0 != ret)
{
//注意释放设备号
unregister_chrdev_region(devno,1);
printk("Unregister char device.\n");
return ret;
}
//down hardware
return 0;
}
其中cdevice、cdev_ops不能定义为局部变量,因为这2个结构体是要在驱动(模块)加载完成后也需要长期存在,要挂载进内核,即要在hello_init函数执行完后也存在,若定义为hello_init函数中的局部变量,则函数执行完就释放掉了。
另外在cdev_add的异常分支注意需要释放设备号。
字符设备驱动卸载函数
字符设备驱动卸载函数也分成2层,和加载函数流程相反,先释放硬件资源,再从内核中注销,注销分成2部:1、从内核中删除字符设备结构体,2、注销设备号
其中从内核中删除字符设备结构体如下
#include <linux/cdev.h>
void cdev_del(struct cdev *cdevice)
卸载函数如下:
//卸载函数(必须)
static void hello_exit(void)//返回值是void类型,函数名自定义,参数是void
{
cdev_t devno = MKDEV(MA, MI);
printk(KERN_ALERT "Goodbye World.\n");
// down hardware
// up kernel
//1、从内核中删除字符设备结构体
cdev_del(cdevice);
//2、注销设备号
unregister_chrdev_region(devno, 1);
}
字符设备操作
要对字符设备进行操作,可以像对文件操作,比如打开、关闭、读写等等,也就是对struct file_operations cdev_ops赋值,其成员是函数指针,赋值如下:
static int my_open(struct inode *pnode, struct file *pfile)
{
printk("Open cdev.\n");
return 0;
}
static int my_close(struct inode *pnode, struct file *pfile)
{
printk("Close cdev.\n");
return 0;
}
struct file_operations cdev_ops = {
.open = my_open,
.release = my_close,
};
那怎么把字符设备、操作、设备文件、应用(App)联系起来呢,明天更新。