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

正点原子RK3568学习日志18-一个驱动兼容不同设备

1.一个驱动兼容不同设备

函数原型

​ container_of(ptr,type,member)

函数作用

​ 通过结构体变量中某个成员的首地址获取到整个结构体变量的首地址。

参数含义

​ ptr是结构体变量中某个成员的地址。

​ type是结构体的类型

​ member是该结构体变量的具体名字


container_of宏的作用是通过结构体内某个成员变量的地址和该变量名,以及结构体类型。找到该结构体变量的地址

struct device_test dev1;//定义设备结构体变量dev1
struct device_test dev2;//定义设备结构体变量dev2//open函数实现,在那个file结构体找
static int cdev_test_open(struct inode *inode, struct file *file)
{dev1.minor = 0;  //次设备号赋值为0dev2.minor = 1;  //次设备号赋值为1//inode->i_rdev 为该 inode 的设备号,使用container_of函数找到结构体变量dev1 dev2的地址
//然后设置私有数据file->private_data = container_of(inode->i_cdev, struct device_test, cdev_test); printk("This is cdev_test_open\r\n");return 0;
}if (test_dev->minor == 0) //次设备号为0{if (copy_to_user(buf, test_dev->kbuf, strlen(test_dev->kbuf)) != 0) //从内核空间拷贝数据到用户空间{printk("copy_to_user is error\n");return -1;}}else if (test_dev->minor == 1) //次设备号为1{if (copy_to_user(buf, test_dev->kbuf, size) != 0) //从内核空间拷贝数据到用户空间{printk("copy_to_user is error\n");return -1;}}if (test_dev->minor == 0) //次设备号为0{if (copy_from_user(test_dev->kbuf, buf, size) != 0)//从用户空间拷贝数据到内核空间{printk("copy_from_user is error\n");return -1;}printk("test_dev->kbuf = %s\r\n", test_dev->kbuf);}else if (test_dev->minor == 1) //次设备号为1{if (copy_from_user(test_dev->kbuf, buf, size) != 0)//从用户空间拷贝数据到内核空间{printk("copy_from_user is error\n");return -1;}printk("test_dev->kbuf = %s\r\n", test_dev->kbuf);}

2.实验:11_container_of  一个驱动兼容不同设备实验

container_of.c

#include <linux/init.h>  //初始化头文件
#include <linux/module.h>  //基本模块文件
#include <linux/kdev_t.h>  //包含设备号相关宏和函数
#include <linux/cdev.h>  //字符设备结构体头文件cdev
#include <linux/fs.h>   //注册设备节点的文件结构体
#include <linux/uaccess.h> //用户空间和内核空间数据传输头文件// //设备号dev_t,设备cdev
// static dev_t dev_num;  //定义32位的变量dev_num,用于存放设备号
// static int major; //定义主设备号变量
// static int minor; //定义次设备号变量
// static struct cdev cdev_test; //定义cdev结构体类型的变量cdev_test// //设备节点
// static struct class *class; //表示要创建的类
// static struct device *device; //表示要创建的设备struct device_test{dev_t dev_num;   //设备号int major;int minor;struct cdev cdev_test;struct class *class;struct device *device;char kbuf[32];};struct device_test dev1;//定义设备结构体变量dev1
struct device_test dev2;//定义设备结构体变量dev2//open函数实现,在那个file结构体找
static int cdev_test_open(struct inode *inode, struct file *file)
{dev1.minor = 0;  //次设备号赋值为0dev2.minor = 1;  //次设备号赋值为1//inode->i_rdev 为该 inode 的设备号,使用container_of函数找到结构体变量dev1 dev2的地址
//然后设置私有数据file->private_data = container_of(inode->i_cdev, struct device_test, cdev_test); printk("This is cdev_test_open\r\n");return 0;
}//read函数,从设备读取数据
static ssize_t cdev_test_read (struct file *file, char __user *buf, size_t size, loff_t *off)
{struct device_test *test_dev = (struct device_test *)file->private_data; //获取设备结构体变量地址if (test_dev->minor == 0) //次设备号为0{if (copy_to_user(buf, test_dev->kbuf, strlen(test_dev->kbuf)) != 0) //从内核空间拷贝数据到用户空间{printk("copy_to_user is error\n");return -1;}}else if (test_dev->minor == 1) //次设备号为1{if (copy_to_user(buf, test_dev->kbuf, size) != 0) //从内核空间拷贝数据到用户空间{printk("copy_to_user is error\n");return -1;}}return 0;
}//write函数,向设备写入数据	
static ssize_t cdev_test_write (struct file *file, const char __user *buf, size_t size, loff_t *off)
{struct device_test *test_dev = (struct device_test *)file->private_data; //获取设备结构体变量地址if (test_dev->minor == 0) //次设备号为0{if (copy_from_user(test_dev->kbuf, buf, size) != 0)//从用户空间拷贝数据到内核空间{printk("copy_from_user is error\n");return -1;}printk("test_dev->kbuf = %s\r\n", test_dev->kbuf);}else if (test_dev->minor == 1) //次设备号为1{if (copy_from_user(test_dev->kbuf, buf, size) != 0)//从用户空间拷贝数据到内核空间{printk("copy_from_user is error\n");return -1;}printk("test_dev->kbuf = %s\r\n", test_dev->kbuf);}return 0;
}static int cdev_test_release (struct inode *inode, struct file *file)
{printk("This is cdev_test_release\n");return 0;
}//设备操作函数
static struct file_operations cdev_test_fops  = {  //file_operations结构体.owner = THIS_MODULE,    //指向本模块.open = cdev_test_open,  //指向cdev_test_open函数.read = cdev_test_read,  //指向cdev_test_read函数.write = cdev_test_write, //指向cdev_test_write函数.release = cdev_test_release, //指向cdev_test_release函数
};static int __init chr_fops_init(void)
{//注册字符驱动int ret; 
//1.创建设备号//动态申请设备号,2个设备号ret = alloc_chrdev_region(&dev1.dev_num, 0, 2, "alloc_name"); if(ret < 0) //申请失败{printk("alloc_chrdev_region is error\n");}printk("alloc_chrdev_region is ok\n");       dev1.major = MAJOR(dev1.dev_num);  //获取主设备号dev1.minor = MINOR(dev1.dev_num);  //获取次设备号printk("major = %d\n", dev1.major);  //打印设备号printk("minor = %d\n", dev1.minor); //2.注册字符设备cdev //初始化cdev//使用cdev_init()初始化cdev_test结构体,并且连接到cdev_test_ops结构体cdev_init(&dev1.cdev_test, &cdev_test_fops); dev1.cdev_test.owner = THIS_MODULE; //将owner字段指向本模块,防止模块被卸载//添加cdevcdev_add(&dev1.cdev_test, dev1.dev_num, 1); //注册字符设备到内核//3.创建设备节点//创建类和设备节点dev1.class = class_create(THIS_MODULE, "test1"); //创建类,名字为testdev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test1"); //创建设备,名字为test//第二个设备cdevdev2.major = MAJOR(dev1.dev_num + 1);  //获取主设备号dev2.minor = MINOR(dev1.dev_num + 1);  //获取次设备号printk("major = %d\r\n", dev2.major);  //打印设备号printk("minor = %d\r\n", dev2.minor); cdev_init(&dev2.cdev_test, &cdev_test_fops); dev2.cdev_test.owner = THIS_MODULE; //将owner字段指向本模块,防止模块被卸载//添加cdevcdev_add(&dev2.cdev_test, dev1.dev_num+1, 1); //注册字符设备到内核dev2.class = class_create(THIS_MODULE, "test2"); //创建类,名dev2.device = device_create(dev2.class, NULL, dev1.dev_num+1, NULL, "test2"); //创建设备return 0;
}
static void __exit chr_fops_exit(void)
{unregister_chrdev_region(dev1.dev_num, 1); //释放设备号   unregister_chrdev_region(dev1.dev_num+1, 1); //释放设备号       cdev_del(&dev1.cdev_test); //注销字符设备 cdev_del(&dev2.cdev_test); //注销字符设备     device_destroy(dev1.class, dev1.dev_num); //删除设备节点device_destroy(dev2.class, dev1.dev_num+1); //删除设备节点    class_destroy(dev1.class); //删除类   class_destroy(dev2.class); //删除类       
}module_init(chr_fops_init);
module_exit(chr_fops_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 += container_of.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操作

app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char *argv[])
{int fd1;  //设备1文件描述符int fd2;  //设备2文件描述符   char buf1[32] = "nihao, /dev/test1"; //写入设备1的数据缓冲区char buf2[32] = "hello, /dev/test2"; //写入设备2的数据缓冲区fd1 = open("dev/test1", O_RDWR); //打开设备文件/dev/test1if(fd1 < 0) //打开失败{printf("open file error\n");return -1;}write(fd1, buf1, sizeof(buf1)); //将buf1缓冲区的数据写入到dev/test设备文件中close(fd1); //关闭设备文件 对取消文件描述符到文件的映射fd2 = open("dev/test2", O_RDWR); //打开设备文件/dev/test2if(fd2 < 0) //打开失败{printf("open file error\n");return -1;}write(fd2, buf2, sizeof(buf2)); //将buf2缓冲区的数据写入到dev/test设备文件中close(fd2); //关闭设备文件 对取消文件描述符到文件的映射return 0;
}///opt/atk-dlrk3568-5_10_sdk-toolchain/bin/aarch64-buildroot-linux-gnu-gcc -o app app.c -static

驱动编译成驱动模块ko 

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

编译测试程序app

生成的app文件就是之后放在开发板上运行的可执行文件

/opt/atk-dlrk3568-5_10_sdk-toolchain/bin/aarch64-buildroot-linux-gnu-gcc -o app app.c -static

                     

运行驱动编译成模块insmod

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

运行测试程序    ./app

3.问题:

1.私有数据的使用场兼容不同设备,​​​​​

    cdev_add(&dev2.cdev_test, dev1.dev_num+1, 1); //注册字符设备到内核

将其次设备号加1,而主设备号保持不变。

dev_num+1 的目的就是为了给驱动程序管理的第二个设备分配一个与第一个设备不同的、唯一的次设备号,从而让内核和用户空间能够准确地区分和访问这两个不同的设备

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

相关文章:

  • SpringBoot-Web开发之静态资源管理
  • 广州做网站代理商建立网站的目录结构应注意哪些问题
  • 初识C语言12. 结构体(自定义类型的核心工具)
  • webrtc源码走读(二)-QOS-RTT
  • 【Java】线程安全问题
  • 数据结构算法学习:LeetCode热题100-链表篇(下)(随机链表的复制、排序链表、合并 K 个升序链表、LRU 缓存)
  • 网络营销网站 功能南京微信小程序开发制作
  • 域名打不开原来的网站手机免费网站制作
  • 中药饮片网购是什么?主要的市场特点及未来发展潜力如何?
  • 自己做的网站数据库360优化大师app
  • Python-__init__函数
  • 沈阳网站维护公司网站建设财务怎么入账
  • JavaEE:知识总结(一)
  • 各家高性能MCU的内置Flash逐渐走向MRAM之路,关于嵌入式 MRAM 的性能和能效
  • Leetcode 35
  • GPIO口输出
  • 专教做美食的网站胶州哪里有做网站的
  • 企业网站制作 徐州网站设计原则的历史
  • 隐私保护与数据安全合规(十三)
  • 2025年高真空共晶炉排名
  • 网站做转链接违反版权吗wordpress页面不显示子类
  • 5.3类的构造方法
  • 视频监控系统原理与计量
  • 蓝桥杯高校新生编程赛第一场题解——Java
  • JavaScript 的优势和劣势是什么?
  • 鸿蒙Next的Camera Kit:开启全场景智慧影像开发新纪元
  • 软件开发包含网站开发吗搭建网站成本
  • asp.net 微网站开发教程比较大的建站公司
  • h5游戏免费下载:小猪飞飞
  • 基于单片机的档案库房漏水检测报警labview上位机系统设计