目录
(一)上篇回顾:上一篇讲到如何加载自己的hello驱动模块。Hello,World驱动之旅,初识驱动模块(一)-CSDN博客
(一)上篇回顾:上一篇讲到如何加载自己的hello驱动模块。Hello,World驱动之旅,初识驱动模块(一)-CSDN博客
- 我们通过指令insmod加载hello.ko
- module_init对应的函数会被执行调用。
- 通过dmesg查看内核LOG,发现hello_init中“hello, world!”有打印
- 通过rmmod卸载hello驱动模块
- 通过dmesg查看内核LOG,发现hello_exit中“see you agin”有打印
(二)驱动模块的意义是什么?
我们的hello驱动莫i狂,仅仅打印一行Log吗?当然不是,虽然目前仅打印了一行Log,不过这已经很厉害了,这个已经是跟内核打交道了,我们的模块已经是操作系统的一员了。所以我们继续努力,丰富我们的Hello模块,让他具备更多的功能。
总所周知,Linux的设计哲学中,有一条核心原则:一切皆文件。这一原则意味着在Linux中,几乎所有的资源和设备都以文件的形式进行表示和访问,我们的hello驱动模块当然也不例外,我们需要为我们的hello模块打开对外服务的大门,通过文件节点的方式提供,这样上层应用才能访问到我们的驱动服务,否则我们的hello模块毫无意义。抛开具体字符设备、块设备、网络设备不谈,我们先通过其他简单方式打开该通过。
(三)hello模块升级,常见class属性文件
通过class_create创建struct class类,"cdevhelloclass"文件讲被创建,位于/sys/class目录下
hellodev.chardev_class = class_create(THIS_MODULE,"cdevhelloclass");
进入Ubuntu命令行,加载模块,执行ls命令,查看/sys/class目录,已生成cdevhelloclass。
通过class_create_file创建class类的属性文件
ret = class_create_file(hellodev.chardev_class, &class_attr_test);
进入Ubuntu命令行,加载模块,执行ls命令,查看/sys/class/cdevhelloclass目录,已生成test文件
(四)添加属性文件功能
class_create_file提供了属性文件的读写方法,可以通过读/写实现与应用的沟通作用,我们再store方法中对全局变量做了+1操作,在show方法中对全局变量进行了显示操作,也就是每写一次文件,我们再进行读取操作,我们将能获取到该文件节点总的写入次数。
static ssize_t test_show(struct class *class, struct class_attribute *attr,
char *buf)
{
int len;
len = sprintf(buf,"test write count:%d\n",hellodev.count);
printk("%s\n",buf);
return len;
}
static ssize_t test_store(struct class *class, struct class_attribute *attr,
const char *buf, size_t count)
{
hellodev.count++;
return count;
}
static CLASS_ATTR_RW(test);
在读写测试我们之前,我们先要修改下文件的读写权限sudo chmod 666 test,再执行ls -la,查看属性已切换至rw-rw-rw(原文件属性为644,即rw-r--r--),这样我们普通用户也有了对该文件的读写操作,否则只能以root用户身份执行该操作。
最后,我们通过我们尝试通过命令echo以及cat对test文件进行读写测试,如下:
至此,大功告成,外界已经可以通过echo以及cat与我们驱动程序进行通信了,我们已经搭建了最基础的内核与应用之间沟通的桥梁。
(五)完整代码如下:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#define MEMORY_SIZE 4096
struct hello_cdev
{
struct class *chardev_class;
char *buf;
int count;
};
static struct hello_cdev hellodev;
static ssize_t test_show(struct class *class, struct class_attribute *attr,
char *buf)
{
int len;
len = sprintf(buf,"test write count:%d\n",hellodev.count);
printk("%s\n",buf);
return len;
}
static ssize_t test_store(struct class *class, struct class_attribute *attr,
const char *buf, size_t count)
{
hellodev.count++;
return count;
}
static CLASS_ATTR_RW(test);
static int hello_init(void)
{
int ret;
hellodev.count = 0;
hellodev.buf = (char *)vmalloc(MEMORY_SIZE);
if(hellodev.buf == NULL ){
goto error;
}
memset(hellodev.buf,0x00,MEMORY_SIZE);
sprintf(hellodev.buf,"test write count:%d",hellodev.count);
hellodev.chardev_class = class_create(THIS_MODULE,"cdevhelloclass");
if(IS_ERR(hellodev.chardev_class))
{
printk("Err: failed in creating class.\n");
goto calss_error;
}
ret = class_create_file(hellodev.chardev_class, &class_attr_test);
if (ret) {
printk("could not create sysfs files\n");
goto class_file_error;
}
printk("hello, world!\r\n");
return 0;
class_file_error:
class_destroy(hellodev.chardev_class);
calss_error:
vfree(hellodev.buf);
error:
return -1;
}
static void hello_exit(void)
{
class_remove_file(hellodev.chardev_class, &class_attr_test);
class_destroy(hellodev.chardev_class);/* 删除类 */
vfree(hellodev.buf);
printk("see you agin\r\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL"); //MODULE_LICENSE
MODULE_AUTHOR("DriverDesigner"); //声明作者信息