字符驱动模板介绍
编写Linux字符设备驱动,核心是实现file_operations里面的函数,通过编写这些函数让设备驱动实现读写等相关操作。通过系统调用函数,实现用户层和内核的数据交互。从而实现应用程序通过驱动对硬件进行操作。
实现file_operations 结构体
实现这个结构体的owner成员一般默认配置为THIS_MODULE。这个成员是必不可少的。
后面的函数分别对应打开、读、写和关闭驱动的操作。在驱动编写中,需要将自己的函数名作为参数传给相应的成员,例如chrdevbase_open这个函数对应的是在打开这个驱动时所做的操作,因此需要将这个函数名传给open这个成员。需要注意的是在驱动中关闭函数对应的是release成员。在file_operations结构体中还有许多函数本文没有涉及,但实现的方法类似。
static struct file_operations chrdevbase_fops = {
.owner = THIS_MODULE,
.open = chrdevbase_open,
.read = chrdevbase_read,
.write = chrdevbase_write,
.release = chrdevbase_release,
};
驱动出入口函数
在驱动挂载和移除时所需要执行的操作函数叫做驱动的入口函数和出口函数。一般这两个函数承载着注册字符设备和注销字符设备的任务。注意,出口函数和入口函数需要将函数名作为参数传入module_init()和module_exit()这两个系统函数中,这样在挂载和移除设备驱动时才会执行相应的函数,具体实现如下。
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
函数
打开驱动函数
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int chrdevbase_open(struct inode *inode, struct file *filp)
{
//printk("chrdevbase open!\r\n");
return 0;
}
读取设备数据
该函数通过调用memcpy()函数,将内核态的数组kerneldata[]中的数据拷贝出来,从而实现用户程序获取内核态的数据。
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue = 0;
/* 向用户空间发送数据 */
memcpy(readbuf, kerneldata, sizeof(kerneldata));
retvalue = copy_to_user(buf, readbuf, cnt);
if(retvalue == 0){
printk("kernel senddata ok!\r\n");
}else{
printk("kernel senddata failed!\r\n");
}
//printk("chrdevbase read!\r\n");
return 0;
}
向设备写入数据
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue = 0;
/* 接收用户空间传递给内核的数据并且打印出来 */
retvalue = copy_from_user(writebuf, buf, cnt);
if(retvalue == 0){
printk("kernel recevdata:%s\r\n", writebuf);
}else{
printk("kernel recevdata failed!\r\n");
}
//printk("chrdevbase write!\r\n");
return 0;
}
关闭/释放设备
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int chrdevbase_release(struct inode *inode, struct file *filp)
{
//printk("chrdevbase release!\r\n");
return 0;
}
驱动入口函数
/*
* @description : 驱动入口函数
* @param : 无
* @return : 0 成功;其他 失败
*/
static int __init chrdevbase_init(void)
{
int retvalue = 0;
/* 注册字符设备驱动 */
retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops);
if(retvalue < 0){
printk("chrdevbase driver register failed\r\n");
}
printk("chrdevbase init!\r\n");
return 0;
}
驱动出口函数
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit chrdevbase_exit(void)
{
/* 注销字符设备驱动 */
unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME);
printk("chrdevbase exit!\r\n");
}
添加模块的许可证声明
MODULE_LICENSE("GPL");