源代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
/* 1. 确定主设备号 */
static int major = 0;
static char kernel_buf[1024];
static struct class *hello_class;
#define MIN(a, b) (a < b ? a : b)
/* 3. 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t hello_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_from_user(kernel_buf, buf, MIN(1024, size));
return MIN(1024, size);
}
static int hello_drv_open (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int hello_drv_close (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 2. 定义自己的file_operations结构体 */
static struct file_operations hello_drv = {
.owner = THIS_MODULE,
.open = hello_drv_open,
.read = hello_drv_read,
.write = hello_drv_write,
.release = hello_drv_close,
};
/* 4. 把file_operations结构体告诉内核:注册驱动程序 */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init hello_init(void)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
major = register_chrdev(0, "hello", &hello_drv); /* /dev/hello */
hello_class = class_create(THIS_MODULE, "hello_class");
err = PTR_ERR(hello_class);
if (IS_ERR(hello_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "hello");
return -1;
}
device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */
return 0;
}
/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 */
static void __exit hello_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(hello_class, MKDEV(major, 0));
class_destroy(hello_class);
unregister_chrdev(major, "hello");
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
分析函数构成
1. file_operation结构体
static struct file_operations hello_drv = {
.owner = THIS_MODULE,
.open = hello_drv_open,
.read = hello_drv_read,
.write = hello_drv_write,
.release = hello_drv_close,
};
定义驱动自己本身的file_operations结构体,里面有具体的对于文件的操作。其每个成员都是一个函数,有自己的参数。
2. 具体操作
static ssize_t hello_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_to_user(buf, kernel_buf, MIN(1024, size));
return MIN(1024, size);
}
该函数格式可以从内核文件中得到。
- 在应用程序中我们一般用 printf() 函数打印, 在驱动程序中我们一般使用 printk() 函数打印。
- 其中 `
__FILE__, __FUNCTION__, __LINE__
都是内核内置的宏,分别表示文件,函数,行号等信息
- _user 为一个空的宏,用来表示用户空间
- 多利用宏来表示一些算式,很方便
#define MIN(a, b) (a < b ? a : b)
- 注意驱动程序和应用程序之间传递数据要用 copy_from_user/copy_to_user 函数。
3. 注册函数
static int __init hello_init(void)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
major = register_chrdev(0, "hello", &hello_drv); /* /dev/hello */
hello_class = class_create(THIS_MODULE, "hello_class");
err = PTR_ERR(hello_class);
if (IS_ERR(hello_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "hello");
return -1;
}
device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */
return 0;
}
顾名思义,将file_operations结构体注册到内核中去,下面我们来看这几个函数
major = register_chrdev(0, "hello", &hello_drv);
第一个参数:可以自己指定主设备号,也可以传入 0 ,让内核给驱动程序分配主设备号;
第二个参数:表示该类设备的名称;
第三个参数:该驱动的file_operations结构体的指针。
最后将主设备号传给 “major”
hello_class = class_create(THIS_MODULE, "hello_class");
创建一个名字叫 hello_class 的类,这个函数将创建一个名字叫 hello_class 的class 类型的结构体并传给hello_class,这个类目前我也不知道干什么的,后面补充……
device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */
创建这个名字叫 hello 的设备节点 /dev/hello 文件
第一个参数:就是上面刚刚创建好的 hello_class;
第三个参数:MKDEV()函数是将 设备主/次设备号转换为 32位或其他长度的dev_t类型的书,
第五个参数:将要在 /dev 创建的文件名字叫 hello
4.卸载驱动
和注册函数反着来就对了
不过,device_distroy() 函数在 calss_distory() 函数前面。
5.其他完善
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
将 hello_init 和 hello_exit 修饰为入/出口函数
最后要声明该驱动函数遵循 GPL 协议,否则没有办法使用,需要开放源代码。【GPL 协议的传染性】