当前位置: 首页 > news >正文

正点原子RK3568学习日志12-注册字符设备

1.注册字符设备

驱动编译成驱动模块ko  运行驱动编译成模块insmod

“内核源码/include/linux/cdev.h”文件——struct cdev 结构体
dev 记录了字符设备号、内核对象、文件操作file_operations结构体(设备的打开、读写、关闭等操作接口)等信息

struct cdev { struct kobject kobj;                  //内嵌的内核对象.struct module *owner;                 //该字符设备所在的内核模块的对象指针.const struct file_operations *ops;    //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.struct list_head list;                //用来将已经向内核注册的所有字符设备形成链表.dev_t dev;                            //字符设备的设备号,由主设备号和次设备号构成.unsigned int count;                   //隶属于同一主设备号的次设备号的个数.
};

“内核源码/include/linux/cdev.h”文件,“内核源码/include/fs/char_dev.c”文件——设备初始化所用到的函数为cdev_init(),

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{memset(cdev, 0, sizeof *cdev);//将整个结构体清零;INIT_LIST_HEAD(&cdev->list);//初始化list成员使其指向自身;kobject_init(&cdev->kobj, &ktype_cdev_default);//初始化kobj成员;cdev->ops = fops;//初始化ops成员,建立cdev 和 file_operations之间的连接
}

内核源码/include/linux/cdev.h”文件——字符设备添加所用到的函数为cdev_add()

1.字符设备初始化

设备初始化所用到的函数为cdev_init()

void cdev_init(struct cdev *, const struct file_operations *);

初始化传入的cdev 类型的结构体,并与自定义的file_operations * 类型的结构体进行链接。

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{memset(cdev, 0, sizeof *cdev);//将整个结构体清零;INIT_LIST_HEAD(&cdev->list);//初始化list成员使其指向自身;kobject_init(&cdev->kobj, &ktype_cdev_default);//初始化kobj成员;cdev->ops = fops;//初始化ops成员,建立cdev 和 file_operations之间的连接
}

要传入的cdev类型结构体,为要初始化的字符设备

要传入的file_operations *   file_operations结构体

2.字符设备的添加

该函数向内核注册一个struct cdev结构体

int cdev_add(struct cdev *, dev_t, unsigned);

struct cdev 类型的结构体

申请的字符设备号

和该设备关联的设备编号的数量

*添加成功返回0,添加失败返回负数


字符设备的注销

 该函数会向内核删除一个struct cdev 类型结构体

void cdev_del(struct cdev *);

要删除的struct cdev 类型的结构体

卸载,先删注册设备,再删设备号

struct cdev cdev_test; //定义cdev结构体类型的变量cdev_test
struct file_operations cdev_test_ops = { //owner字段指向本模块,定义file_operations结构体类型的变量cdev_test_ops.owner = THIS_MODULE
};
    //使用cdev_init()初始化cdev_test结构体,并且连接到cdev_test_ops结构体cdev_init(&cdev_test, &cdev_test_ops); cdev_test.owner = THIS_MODULE; //将owner字段指向本模块,防止模块被卸载ret = cdev_add(&cdev_test, dev_num, 1); //注册字符设备到内核
    cdev_del(&cdev_test); //注销字符设备  先删除设备,再删除设备号

2.实验:05_cdev 注册字符设备

动态申请设备号的方式进行设备号的申请,然后对设备进行注册

cdev.c     Makefile

cdev.c 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>static dev_t dev_num;  //定义32位的变量dev_num,用于存放设备号struct cdev cdev_test; //定义cdev结构体类型的变量cdev_test
struct file_operations cdev_test_ops = { //owner字段指向本模块,定义file_operations结构体类型的变量cdev_test_ops.owner = THIS_MODULE
};static int __init module_cdev_init(void)
{int ret; //判断函数返回值int major; //定义主设备号变量int minor; //定义次设备号变量//动态申请设备号ret = alloc_chrdev_region(&dev_num, 0, 1, "chrdev_name"); if(ret < 0) //申请失败{printk("alloc_chrdev_region is error\n");}printk("alloc_chrdev_region is ok\n");       major = MAJOR(dev_num);  //获取主设备号minor = MINOR(dev_num);  //获取次设备号printk("major = %d\n", major);  //打印设备号printk("minor = %d\n", minor); //使用cdev_init()初始化cdev_test结构体,并且连接到cdev_test_ops结构体cdev_init(&cdev_test, &cdev_test_ops); cdev_test.owner = THIS_MODULE; //将owner字段指向本模块,防止模块被卸载ret = cdev_add(&cdev_test, dev_num, 1); //注册字符设备到内核if (ret < 0) //注册失败{printk("cdev_add is error\n");}printk("cdev_add is ok\n");return 0;
}
static void __exit module_cdev_exit(void)
{cdev_del(&cdev_test); //注销字符设备  先删除设备,再删除设备号unregister_chrdev_region(dev_num, 1); //释放设备号printk("module exit. \n");}
module_init(module_cdev_init);
module_exit(module_cdev_exit);
MODULE_LICENSE("GPL v2");   //声明模块许可证
MODULE_AUTHOR("AFANFAN");   //声明模块作者

Makefile

export ARCH=arm64#设置平台架构
export CROSS_COMPILE=/opt/atk-dlrk3568-5_10_sdk-toolchain/bin/aarch64-buildroot-linux-gnu-#交叉编译器前缀
obj-m += cdev.o    #此处要和你的驱动源文件同名
KDIR :=/home/alientek/rk3568_linux5.10_sdk/kernel    #这里是你的内核目录                                                                                                                            
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modules    #make操作
clean:make -C $(KDIR) M=$(PWD) clean    #make clean操作

驱动编译成驱动模块ko 

使用命令“make”进行驱动的编译,编译完生成cdev.ko目标文件

运行驱动编译成模块insmod

开发板启动之后, insmod cdev.ko

注册设备号的查看  cat /proc/devices

对驱动进行卸载   rmmod cdev.ko

3.问题:

1.__init  优化内存使用

在Linux内核源代码中,__init 是一个特殊的宏,它用来告诉编译器,被它修饰的函数(在这里是 dev_t_init)或数据结构只在内核初始化期间被使用

内核在编译和链接时会将它们统一放置在一个特殊的内存区域(.init.text 段)。在内核启动过程的最后阶段,内核会释放这部分内存区域,将其回收给系统使用

与 __init 类似的,还有一些其他的宏:

__exit:用于修饰模块卸载时执行的函数。对于静态编译进内核的代码,如果配置了允许卸载,它也会被特殊处理。

__initdata:用于修饰只在初始化阶段使用的数据。这些数据也会被放在特殊的内存段,并在初始化后被释放。

__exitdata:用于修饰模块卸载时使用的数据。

http://www.dtcms.com/a/508052.html

相关文章:

  • zookeeper简介
  • 注册中心对比 -- eureka、nacos、consul、zookeeper、redis过期key
  • php 茶叶网站网页qq登录保护怎么关闭
  • 做南美生意做什么网站好网站维护需要多久时间
  • MFC 在list右键弹出菜单栏功能 ,在list控件自定义绘制按钮控件
  • 网站设计中的事件是什么宝钢工程建设有限公司网站
  • vue3 之 基础+核心概念+上手技巧
  • 兰州网站建设推荐q479185700顶上北京邢台企业商会网站
  • TypeScript基础入门与数据类型
  • PHP面试题——情景应用
  • 看门狗设置
  • 部门网站建设总结网上商城网站建设
  • 做网站服务器哪种好外贸企业网站推广方案
  • 合肥企业网站推广英文网站建设情况
  • MVVM 架构 android
  • 数据结构8:栈
  • 激活函数只是“非线性开关“?ReLU、Sigmoid、Leaky ReLU的区别与选择
  • C# 基础——多态的实现方式
  • 【Nginx反向代理技术详解】原理、配置与实践
  • 福州企业网站维护价格低网站建设人员的安排
  • icon图标素材下载网站网络营销推广策划的步骤
  • ObjectId objectId = gridFSTemplate.store(fileInputStream, “文件轮播对象“, ““)
  • SpringBoot的actuator组件快速使用
  • STM32学习(MCU控制)(GPIO)
  • wordpress站点标题添加如何注册一个自己的公司
  • 台州企业网站搭建价格网站开发的交付文档
  • 橙色守护者:嘉顺达蓝海的危险品运输安全密码
  • 安全监控摄像头通过智能组网模块实现联网报警和远程管理的实践解析
  • AI时代的数据管理新范式:Git for Data让数据工程化
  • Linux中内核调用用户空间程序的实现