【Linux】【驱动】自动创建设备节点
- 续
- 驱动代码
- 操作指令
- linux端
- 从机端
续
这里展示了如何自动的方式去创建一个字符类的节点
下面就是需要调用到的程序
函数
void cdev_init(struct cdev *, const struct file_operations *);
第一个参数 要初始化的 cdev
第二个参数 文件操作集 cdev->ops = fops; //实际就是把文件操作集写给 ops
功能 cdev_init()函数用于初始化 cdev 的成员,并建立 cdev 和 file_operations 之间的连接。
函数
int cdev_add(struct cdev *, dev_t, unsigned);
第一个参数 cdev 的结构体指针
第二个参数 设备号
第三个参数 次设备号的数量
功能 cdev_alloc()函数用于动态申请一个 cdev 内存
void cdev_del(struct cdev *);
cdev 的结构体指针
生成设备节点
字符设备注册完以后不会自动生成设备节点。我们需要使用 mknod 命令创建一个设备节点
格式:mknod 名称 类型 主设备号 次设备号
mknod /dev/test c 247 0
驱动代码
代码实现的流程
从 hello_init 函数开始看,
- 注册设备号,
- 初始化 cdev
- 向系统注册设备
- 创建 class 类
- 在 class 类下创建设备
从hello_exit 来看
- 注销设备号
- 删除设备
- 注销设备
- 删除类
#include <linux/init.h>
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/fs.h> //包含了文件操作相关 struct 的定义,例如大名鼎鼎的 struct file_operations
#include <linux/kdev_t.h>
#include <linux/cdev.h> //对字符设备结构 cdev 以及一系列的操作函数的定义。//包含了 cdev 结构及相关函数的定义。
#include <linux/device.h> //包含了 device、class 等结构的定义
#define DEVICE_NUMBER 1 //定义次设备号的个数
#define DEVICE_SNAME "schrdev" //定义静态注册设备的名称
#define DEVICE_ANAME "achrdev" //定义动态注册设备的名称
#define DEVICE_MINOR_NUMBER 0 //定义次设备号的起始地址
#define DEVICE_CLASS_NAME "chrdev_class" //宏定义类名
#define DEVICE_NODE_NAME "chrdev_test" //宏定义设备节点的名字
static int major_num, minor_num; //定义主设备号和次设备号
struct class *class; //定义类
struct device *device; /* 设备 */
struct cdev cdev;//定义一个 cdev 结构体
module_param(major_num,int,S_IRUSR); //驱动模块传入普通参数 major_num
module_param(minor_num ,int,S_IRUSR);//驱动模块传入普通参数 minor_num
dev_t dev_num;
/**
* @description: 打开设备
* @param {structinode} *inode:传递给驱动的 inode
* @param {structfile} *file:设备文件,file 结构体有个叫做 private_data 的成员变量,
* 一般在 open 的时候将 private_data 指向设备结构体。
* @return: 0 成功;其他 失败
*/
int chrdev_open(struct inode *inode, struct file *file)
{
printk("chrdev_open\n");
return 0;
}
// 设备操作函数结构体
struct file_operations chrdev_ops = {
.owner = THIS_MODULE,
.open = chrdev_open};
/**
* @description: 驱动入口函数
* @param {*}无
* @return {*} 0 成功;其他 失败
*/
static int hello_init(void)
{
int ret;//函数返回值
if(major_num)
{
/*静态注册设备号*/
printk("major_num = %d\n",major_num);//打印传入进来的主设备号
printk("minor_num = %d\n",minor_num);//打印传入进来的次设备号
dev_num = MKDEV(major_num,minor_num);//MKDEV 将主设备号和次设备号合并为一个设备号
ret = register_chrdev_region(dev_num, DEVICE_NUMBER,DEVICE_SNAME);//注册设备号
if(ret<0)
{
printk("register_chrdev_region error\n");
}
//静态注册设备号成功,则打印。
printk("register_chrdev_region ok\n");
}
else
{
/*动态注册设备号*/
ret = alloc_chrdev_region(&dev_num,DEVICE_MINOR_NUMBER,1, DEVICE_ANAME);
if(ret<0)
{
printk("alloc_chrdev_region error\n");
}
//动态注册设备号成功,则打印
printk("alloc_chrdev_region ok\n");
major_num =MAJOR(dev_num); //将主设备号取出来
minor_num = MINOR(dev_num);//将次设备号取出来
printk("major_num = %d\n",major_num);//打印传入进来的主设备号
printk("minor_num = %d\n",minor_num);//打印传入进来的次设备号
}
// 初始化 cdev
cdev.owner = THIS_MODULE;
//cdev_init 函数初始化 cdev 结构体成员变量
cdev_init(&cdev, &chrdev_ops);
//完成字符设备注册到内核
cdev_add(&cdev, dev_num, DEVICE_NUMBER);
//创建类
class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
// 在 class 类下创建设备
device = device_create(class, NULL, dev_num, NULL, DEVICE_NODE_NAME);
return 0;
}
//drivers for exit
static void hello_exit(void)
{
//注销设备号
unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER);
//删除设备
cdev_del(&cdev);
//注销设备
device_destroy(class, dev_num);
//删除类
class_destroy(class);
printk("gooodbye! \n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Chris");
下面就是app的代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int fd;
char buf[64] = {0};
fd = open("/dev/chrdev_test",O_RDWR); //打开设备节点
if(fd < 0)
{
perror("open error \n");
return fd;
}
//read(fd,buf,sizeof(buf)); //从文件中读取数据放入缓冲区中
close(fd);
return 0;
}
操作指令
linux端
arm-buildroot-linux-gnueabihf-gcc -o app app.c
cp app /home/book/nfs_rootfs/
从机端
驱动卸载掉,再加载新编译好的的驱动
rmmod chrdev
insmod chrdev.ko
我们输入以下命令查看/sys/class 下面是否生成类,
ls /sys/class/chrdev_class/
查看下是否生成了设备节点
ls /sys/class/
来验证生成的设备节点是否可以使用
./app