创建一个hello的设备来举例应用层如何调用kernel驱动,kernel驱动部分为转载,仅对部分语句做解释。
init函数
Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
register_chrdev用于从内核中为字符设备申请一个主设备号。
创建"hello_class"以及"hello"设备,这两个设备分别挂载在如下路径:
/sys/class/hello_class
/dev/hello
/* 5. kernel启动时initcall(module_init)会调用这个入口函数 */
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 */
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
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;
}
/* 指定驱动的入口和出口,以及声明自己的驱动遵循GPL协议(不声明的话无法把驱动加载进内核) */
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
我们来总结一下file_operations模式的驱动模型,file_operations是内核提供用来操作文件的操作集合,我们示例中只使用到了下面四个函数,因此示例中的hello_drv只实现了这四个函数。
struct file_operations {
...
struct module *owner;
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
...
}
实现了file_operations数据结构后,然后通过module_init/xxx_initcall等方式,调用register_chrdev(仅以字符设备为例),将hello_drv注册到"/dev/"下,例如:
major = register_chrdev(0, "hello", &hello_drv); /* /dev/hello */
即将设备注册到/dev/hello
再通过class_create/device_create创建设备的总线信息及设备节点。
将驱动编译进kernel内核后,我们来写一个app打开该设备,调用设备驱动的接口。
注意一下open函数的设备名称为:"/dev/hello"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
static char databuf[1024];
static char writebuf[1024];
int main(int argc,char *argv[])
{
int fd;
int ret;
fd = open("/dev/hello", O_RDWR);
if(fd < 0){
printf("open fail! \n");
printf("error number: %d \n",fd);
return -1;
}
memset(databuf,0x0,1024);
ret = read(fd, databuf, sizeof(databuf));
memset(writebuf,0x0,1024);
ret = write(fd, writebuf, sizeof(writebuf));
printf("write over \n");
}