例程代码路径:ELF 1开发板资料包3-例程源码3-2 驱动例程源码2_字符驱动mydevice-auto
上一节写的驱动加载后,需要手动使用mknod命令在/dev下创建设备节点,本节讲解如何让驱动自动在/dev目录下生成设备节点。
想要实现自动创建节点,首先需要创建设备类,然后创建设备节点并关联到设备类。
在mydevice.c源码的基础上进行添加,重命名为mydevice-auto.c
(一)添加头文件
#include <linux/device.h> |
(二)声明变量
static struct class *my_class; static struct device *my_device; |
(三)在mydevice_init(void)函数中创建类和设备节点
static int __init mydevice_init(void) { int ret;
// 在这里执行驱动程序的初始化操作
// 注册字符设备驱动程序 ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME); if (ret < 0) { printk(KERN_ALERT "Failed to allocate device number: %dn", ret); return ret; } major=MAJOR(dev_num); //获取主设备号 minor=MINOR(dev_num); //获取次设备号 printk(KERN_INFO "major number: %dn",major); printk(KERN_INFO "minor number: %dn",minor);
my_cdev.owner = THIS_MODULE; cdev_init(&my_cdev,&fops); //初始化字符设备结构体 cdev_add(&my_cdev,dev_num,1); //将字符设备添加到内核中
// 创建设备类 my_class = class_create(THIS_MODULE, "my_class"); if (IS_ERR(my_class)) { pr_err("Failed to create classn"); return PTR_ERR(my_class); } // 创建设备节点并关联到设备类 my_device = device_create(my_class, NULL, MKDEV(major, minor), NULL, DEVICE_NAME); if (IS_ERR(my_device)) { pr_err("Failed to create devicen"); class_destroy(my_class); return PTR_ERR(my_device); } printk(KERN_INFO "Device registered successfully.n"); return 0; }
|
class_create()函数的原型:
struct class *class_create(struct module *owner, const char *name); |
owner:指向拥有该设备类的内核模块的指针。通常使用THIS_MODULE来指定当前驱动模块作为拥有者;
name:指定设备类的名称。该名称将用于在sysfs文件系统中创建对应的类目录;
class_create()函数返回一个指向新创建的设备类的struct class指针。如果创建失败,它将返回一个指向NULL的指针;
设备类的创建是设备驱动程序中重要的一步,它为设备提供了一个统一的命名空间和属性集合,并将相关设备节点组织在一起。创建设备类后,驱动程序可以使用device_create()函数创建设备节点,并将其关联到该设备类下。
device_create()函数的原型:
struct device *device_create(struct class *class, struct device *parent, dev_t dev, void *drvdata, const char *fmt, ...); |
class:指向设备节点所属的设备类的struct class指针;
parent:指向设备节点的父设备的struct device指针,如果没有父设备,则可以设置为NULL;
dev:指定设备节点的设备号,使用MKDEV(major, minor)来创建;
drvdata:指向与设备节点相关的私有数据的指针,可以将驱动程序的特定数据与设备节点关联起来;
fmt:设备节点名称的格式字符串,用于在sysfs文件系统中创建设备节点。可以使用类似"mydevice%d"的格式,并使用可变参数来指定设备节点名称的后缀;
device_create()函数返回一个指向新创建的设备节点的struct device指针。如果创建失败,它将返回一个指向NULL的指针;
设备节点的创建需要先创建一个设备类(使用class_create()函数),然后使用device_create()函数创建设备节点并将其关联到设备类下;
(四)mydevice_exit(void)函数中添加销毁设备节点和设备类
static void __exit mydevice_exit(void) { // 在这里执行驱动程序的清理操作 // 销毁设备节点 device_destroy(my_class, MKDEV(major, minor)); // 销毁设备类 class_destroy(my_class); // 删除字符设备 cdev_del(&my_cdev); // 注销字符设备驱动程序 unregister_chrdev(0, DEVICE_NAME);
printk(KERN_INFO "Device unregistered.n"); }
|
mydevice-auto.c完整示例源码
#include <linux/module.h> // 包含模块相关函数的头文件 #include <linux/fs.h> // 包含文件系统相关函数的头文件 #include <linux/uaccess.h> // 包含用户空间数据访问函数的头文件 #include <linux/cdev.h> //包含字符设备头文件 #include <linux/device.h> #define DEVICE_NAME "mydevice" // 设备名称 static dev_t dev_num; //分配的设备号 struct cdev my_cdev; //字符设备指针 int major; //主设备号 int minor; //次设备号 static struct class *my_class; static struct device *my_device;
static int device_open(struct inode *inode, struct file *file) { // 在这里处理设备打开的操作 printk(KERN_INFO "This is device_open.n"); return 0; }
static int device_release(struct inode *inode, struct file *file) { // 在这里处理设备关闭的操作 printk(KERN_INFO "This is device_release.n");
return 0; }
static ssize_t device_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) { // 在这里处理设备读取的操作 printk(KERN_INFO "This is device_read.n"); return 0; }
static ssize_t device_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) { // 在这里处理设备写入的操作 printk(KERN_INFO "This is device_write.n"); return 0; }
static struct file_operations fops = { .owner = THIS_MODULE, .open = device_open, .release = device_release, .read = device_read, .write = device_write, };
static int __init mydevice_init(void) { int ret;
// 在这里执行驱动程序的初始化操作
// 注册字符设备驱动程序 ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME); if (ret < 0) { printk(KERN_ALERT "Failed to allocate device number: %dn", ret); return ret; } major=MAJOR(dev_num); //获取主设备号 minor=MINOR(dev_num); //获取次设备号 printk(KERN_INFO "major number: %dn",major); printk(KERN_INFO "minor number: %dn",minor);
my_cdev.owner = THIS_MODULE; cdev_init(&my_cdev,&fops); //初始化字符设备结构体 cdev_add(&my_cdev,dev_num,1); //将字符设备添加到内核中
// 创建设备类 my_class = class_create(THIS_MODULE, "my_class"); if (IS_ERR(my_class)) { pr_err("Failed to create classn"); return PTR_ERR(my_class); } // 创建设备节点并关联到设备类 my_device = device_create(my_class, NULL, MKDEV(major, minor), NULL, DEVICE_NAME); if (IS_ERR(my_device)) { pr_err("Failed to create devicen"); class_destroy(my_class); return PTR_ERR(my_device); } printk(KERN_INFO "Device registered successfully.n"); return 0; }
static void __exit mydevice_exit(void) { // 在这里执行驱动程序的清理操作 // 销毁设备节点 device_destroy(my_class, MKDEV(major, minor)); // 销毁设备类 class_destroy(my_class); // 删除字符设备 cdev_del(&my_cdev); // 注销字符设备驱动程序 unregister_chrdev(0, DEVICE_NAME);
printk(KERN_INFO "Device unregistered.n"); }
module_init(mydevice_init); module_exit(mydevice_exit);
MODULE_LICENSE("GPL"); // 指定模块的许可证信息 MODULE_AUTHOR("Your Name"); // 指定模块的作者信息 MODULE_DESCRIPTION("A simple character device driver"); // 指定模块的描述信息 |
编译
Makefile文件如下:
. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi elf@ubuntu:~/work/test/02_字符驱动/mydevice-auto$ make |
将生成的mydevice.ko文件拷贝到开发板中。
编写测试应用源码device-auto_app.c
驱动中已经给应用层提供了open、read、write的接口,接下来应用层就可以进行相应的系统调用。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <errno.h> #include <fcntl.h>
#define DEV_NAME "/dev/my_device"
int main(int argc, char *argv[]) { int reg; int fd = 0; int dat = 0;
fd = open (DEV_NAME, O_RDWR); if (fd < 0) { perror("Open "DEV_NAME" Failed!n"); exit(1); }
reg = read(fd, &dat, 1); if (reg<0) { perror("read "DEV_NAME" Failed!n"); exit(1); }
dat = 0; reg = write(fd, &dat, 1); if (reg<0) { perror("write "DEV_NAME" Failed!n"); exit(1); } close(fd); return 0; } |
设置环境变量:
. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi |
编译,将生成的device-auto_app文件拷贝到开发板中:
elf@ubuntu:~/work/test/02_字符驱动/device-auto_app$ $CC device-auto_app.c -o device-auto_app |
测试
root@ELF1:~# insmod mydevice-auto.ko major number: 245 minor number: 0 Device registered successfully. root@ELF1:~# ls /dev/my_device /dev/my_device root@ELF1:~# ./device-auto_app This is device_open. This is device_read. This is device_write. This is device_release. root@ELF1:~# rmmod mydevice-auto.ko Device unregistered. |
可以看出在使用insmod加载驱动后,在/dev下就生成了my_device节点,使用device_app测试正常。