首页 > 其他分享 >RK3568,字符设备框架:管理同主设备号、不同次设备号设备

RK3568,字符设备框架:管理同主设备号、不同次设备号设备

时间:2023-12-05 17:57:23浏览次数:38  
标签:RK3568 dev KERN dev2 dev1 cdev test 同主 设备

字符设备框架:管理同主设备号、不同次设备号设备

以下代码针对迅为开发板RK3568,开发板系统是ubuntu20.04,

正文

以下是我写的字符设备框架,实现了管理同主设备号、不同次设备号的功能。

代码:

private_data_test.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

/* 一个驱动兼容不同设备实验 */

/*
  定义一个设备结构体,专门存放设备号、设备类、设备类中的设备。显得很规整。
*/
struct device_test
{
    dev_t dev_num;          /* 设备号 */
    int major;              /* 定义 int 类型的主设备号 major */
    int minor;              /* 定义 int 类型的次设备号 minor */
    struct cdev cdev_test;  /* 设备暂起名cdev_test */
    struct class * class ;  /* 类 */
    struct device * device; /* 设备 */
    char kbuffer[64];      /* 缓存区 buf */
};

/* dev1和dev2的主设备号相同,次设备号不同。 */
static struct device_test dev1;
static struct device_test dev2;

static int cdev_test_ops_open (struct inode * inode, struct file * file)
{
    dev1.minor = 0; //设置 dev1 的次设备号为 0
    dev2.minor = 1; //设置 dev2 的次设备号为 1
/*
    container_of 函数
    函数原型: container_of(ptr, type, member)
    函数功能: 通过结构体变量中某个成员的首地址获取到整个结构体变量的首地址。
    函数参数: 第一个参数ptr是结构体变量中某个成员的地址。
            第二个参数type是结构体的类型。
            第三个参数member是该结构体变量的具体名字。
*/
    file->private_data = container_of(inode->i_cdev,struct device_test, cdev_test);//现在程序自动判断是dev1还是dev2
    printk(KERN_ERR "This is cdev_test_ops_open in Kernel\r\n");
    return 0;
};

static ssize_t cdev_test_ops_read (struct file * file, char __user * buf, size_t size, loff_t * off)
{
    struct device_test * test_dev = (struct device_test *)file->private_data;
    int ret = 0;
    ret = copy_to_user(buf, test_dev->kbuffer, strlen(test_dev->kbuffer)); //拷贝结束且健康,返回0
    if(ret != 0) //返回值≠0,就是拷贝到用户态失败。
    {
        printk(KERN_ERR "copy_to_user, failed\r\n");
        return -1;
    }
    printk(KERN_ERR "copy_to_user, successed\r\n");
    printk(KERN_ERR "Kernel Message:This is cdev_test_ops_read\r\n");
    return 0;
};

static ssize_t cdev_test_ops_write (struct file *file , const char __user * buf, size_t size, loff_t * off)
{
    int ret = 0;
    struct device_test * test_dev = (struct device_test *)file->private_data;
    if(test_dev->minor == 0)
    {
        printk(KERN_ERR "This is dev1\r\n");
        ret = copy_from_user(test_dev->kbuffer, buf, size); //拷贝结束且健康,返回0
        if(ret != 0)
        {
            printk(KERN_ERR "copy_from_user, failed\r\n");
            return -1;
        }
        printk(KERN_ERR "copy_from_user, successed\r\n");
        printk(KERN_ERR "test_dev->kbuffer is %s\r\n", test_dev->kbuffer);
    }
    else if(test_dev->minor == 1)
    {
        printk(KERN_ERR "This is dev2\r\n");
        ret = copy_from_user(test_dev->kbuffer, buf, size); //拷贝结束且健康,返回0
        if(ret != 0)
        {
            printk(KERN_ERR "copy_from_user, failed\r\n");
            return -1;
        }
        printk(KERN_ERR "copy_from_user, successed\r\n");
        printk(KERN_ERR "test_dev->kbuffer is %s\r\n", test_dev->kbuffer);
    }
    printk(KERN_ERR "This is cdev_test_ops_write in Kernel\r\n");
    return 0;
};

static int cdev_test_ops_release (struct inode * inode, struct file * file)
{
    printk(KERN_ERR "This is cdev_test_ops_release in Kernel\r\n");
    return 0;
};

static struct file_operations cdev_test_ops = {
    .owner = THIS_MODULE,
    .open = cdev_test_ops_open, //open字段指向 cdev_test_ops_open
    .read = cdev_test_ops_read,
    .write = cdev_test_ops_write,
    .release = cdev_test_ops_release
};

/***************************** 接下来是入口函数 ********************************/

static int __init module_cdev_init(void)
{
    int ret = 0;    //ret value,返回值

    printk(KERN_ERR "Enter Module Init\r\n");

    ret = alloc_chrdev_region(&dev1.dev_num, 0, 2, "dynamically alloc dev1 number");
    if(ret < 0)
    {
        printk(KERN_ERR "apply_cdev1_num, failed\r\n");
    }
    printk(KERN_ERR "apply_cdev1_num, successed\r\n");

    dev1.major = MAJOR(dev1.dev_num);
    dev1.minor = MINOR(dev1.dev_num);
    printk(KERN_ERR "dev1:major is %d,minor is %d\r\n", dev1.major, dev1.minor);

    /* dev1的一套东西 */
    dev1.cdev_test.owner = THIS_MODULE; //将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
    cdev_init(&dev1.cdev_test, &cdev_test_ops);
    ret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1);
    if(ret < 0)
    {
        printk(KERN_ERR "cdev_add1, failed\r\n");
    }
    printk(KERN_ERR "cdev_add1, successed\r\n");

    dev1.class = class_create(THIS_MODULE, "test1_class");
    dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test1_dev"); 

    /* dev2的一套东西 */
    ret = alloc_chrdev_region(&dev2.dev_num, 0, 2, "dynamically alloc dev2 number");
    if(ret < 0)
    {
        printk(KERN_ERR "apply_cdev2_num, failed\r\n");
    }
    dev2.major = MAJOR(dev1.dev_num);
    dev2.minor = MINOR(dev1.dev_num + 1);
    printk(KERN_ERR "dev2:major is %d,minor is %d\r\n", dev2.major, dev2.minor);

    dev2.cdev_test.owner = THIS_MODULE;
    cdev_init(&dev2.cdev_test, &cdev_test_ops);
    ret = cdev_add(&dev2.cdev_test, MKDEV(dev2.major,dev2.minor), 1);
    if(ret < 0)
    {
        printk(KERN_ERR "cdev2_add, failed\r\n");
    }
    printk(KERN_ERR "cdev2_add, successed\r\n");
    dev2.class = class_create(THIS_MODULE, "test2_class"); 
    dev2.device = device_create(dev2.class, NULL, MKDEV(dev2.major,dev2.minor), NULL, "test2_dev");

    return 0;
}

static void __exit  module_cdev_exit(void)
{
    cdev_del(&dev1.cdev_test); //删除添加的字符设备 cdev_test
    cdev_del(&dev2.cdev_test); //删除添加的字符设备 cdev_test

    device_destroy(dev1.class, dev1.dev_num); //删除创建的设备
    device_destroy(dev2.class, MKDEV(dev2.major,dev2.minor)); //删除创建的设备

    class_destroy(dev1.class); //删除创建的类
    class_destroy(dev2.class); //删除创建的类

    unregister_chrdev_region(dev1.dev_num, 1); // 释放字符驱动设备号
    unregister_chrdev_region(dev1.dev_num+1, 1); // 释放字符驱动设备号

    printk(KERN_ERR "module_cdev_exit, bye bye\r\n");
}

module_init(module_cdev_init); //注册入口函数
module_exit(module_cdev_exit); //注册出口函数

MODULE_LICENSE("GPL");
MODULE_AUTHOR("my_topeet_drv");
MODULE_VERSION("v1.0.0");

app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int arc, char *argv[])
{
    int fd1, fd2;
    char buf1[64] = "/dev/test1, hello dev1 from app layer in user space\n";
    char buf2[64] = "/dev/test2, hello dev2 from app layer in user space\n";

    /*设备节点1*/
    fd1 = open("/dev/test1_dev", O_RDWR, 0666);
    if(fd1 < 0)
    {
        perror("open /dev/test1_dev error\n");
        return fd1;
    }
    write(fd1, buf1, sizeof(buf1));
    close(fd1);

    /*设备节点2*/
    fd2 = open("/dev/test2_dev", O_RDWR, 0666);
    if(fd2 < 0)
    {
        perror("open /dev/test2_dev error\n");
        return fd2;
    }
    write(fd2, buf2, sizeof(buf2));
    close(fd2);
    
    return 0;
}

结果

root@topeet:/home/topeet/myko$ make insmod
insmod private_data_test.ko
[   83.775025] Enter Module Init
[   83.775094] apply_cdev1_num, successed
[   83.775103] dev1:major is 236,minor isroot@topeet:/home/topeet/myko$  0
[   83.775115] cdev_add1, successed
[   83.775735] dev2:major is 236,minor is 1
[   83.775765] cdev2_add, successed
./a.out
[   88.615091] This is cdev_test_ops_open in Kernel
[   88.615177] This isroot@topeet:/home/topeet/myko$ dev1
[   88.615188] copy_from_user, successed
[   88.615197] test_dev->kbuffer is /dev/test1, hello dev1 from app layer in user space
[   88.615197]
[   88.615205] This is cdev_test_ops_write in Kernel
[   88.615219] This is cdev_test_ops_release in Kernel
[   88.615245] This is cdev_test_ops_open in Kernel
[   88.615254] This is dev2
[   88.615260] copy_from_user, successed
[   88.615265] test_dev->kbuffer is /dev/test2, hello dev2 from app layer in user space
[   88.615265]
[   88.615271] This is cdev_test_ops_write in Kernel
[   88.615278] This is cdev_test_ops_release in Kernel

遇到的坑:

1、设备2的设备号,也要使用动态分配语句 alloc_chrdev_region 来做;

2、设备节点名字必须不一样(设备节点名在语句 device_create 里修改);

3、动态分配语句 alloc_chrdev_region 里的第四个参数,在两次获取设备号的语句中,建议写为不一样的为好。

标签:RK3568,dev,KERN,dev2,dev1,cdev,test,同主,设备
From: https://www.cnblogs.com/salty-pineapple/p/17877809.html

相关文章

  • 硬件开发笔记(十四):RK3568底板电路LVDS模块、MIPI模块电路分析、LVDS硬件接口、MIPI硬件
    前言  本篇继续分析底板原理图mipi/lvds屏幕电路原理图、硬件接口详解。 LVDS与MIPI的区别  液晶屏有RGBTTL、LVDS、MIPI、HDMI接口,这些接口区别于信号的类型(种类),也区别于信号内容。RGBTTL接口信号类型是TTL电平,信号的内容是RGB666或者RGB888还有行场同步和......
  • GMK15100-ASEMI光伏设备二极管GMK15100
    编辑:llGMK15100-ASEMI光伏设备二极管GMK15100型号:GMK15100品牌:ASEMI正向电流:15A反向耐压:100V封装:批号:2023+引脚数量:2工作温度:-55°C~150°CGMK15100特征:肖特基势垒高二极管;热阻低;正向压降低,功率损耗低隔离包装设计,非常适合散热;高正向电流能力;优异的抗湿性;低调的......
  • 夜莺专业版网络设备功能介绍
    网络设备采集简介网络设备的问题通常会产生较大范围的影响,因此采集监控网络设备是一项常见的任务。不同公司在实施网络设备采集时可能采用不同的方案,主要有三类:SNMP(SimpleNetworkManagementProtocol):SNMP是一种常用的网络管理协议,可以用于获取网络设备的状态和性能信息。大......
  • 基于Java的实验室设备管理系统设计与实现(源码+lw+部署文档+讲解等)
    文章目录前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding)代码参考数据库参考源码获取前言......
  • GMK5050-ASEMI光伏设备二极管GMK5050
    编辑:llGMK5050-ASEMI光伏设备二极管GMK5050型号:GMK5050品牌:ASEMI正向电流:50A反向耐压:50V封装:批号:2023+引脚数量:2工作温度:-55°C~150°CGMK5050特征:肖特基势垒高二极管;热阻低;正向压降低,功率损耗低隔离包装设计,非常适合散热;高正向电流能力;优异的抗湿性;低调的包装;高正向浪涌能力;GMK505......
  • 迅为基于iTOP-RK3568开发板主要讲解输入子系统,共计24 讲。
    驱动视频全新升级,并持续更新~更全,思路更科学,入门更简单。迅为基于iTOP-RK3568开发板进行讲解,本次更新内容为第十三期,主要讲解输入子系统,共计24讲。1.总领:本期视频介绍2.什么是输入子系统?3.如何确定输入设备与节点的对应关系?4.实践:确定输入设备对应的设备节点5.输入子系统框架讲解6......
  • GMK5050-ASEMI光伏设备二极管GMK5050
    编辑:llGMK5050-ASEMI光伏设备二极管GMK5050型号:GMK5050品牌:ASEMI正向电流:50A反向耐压:50V封装:批号:2023+引脚数量:2工作温度:-55°C~150°CGMK5050特征:肖特基势垒高二极管;热阻低;正向压降低,功率损耗低隔离包装设计,非常适合散热;高正向电流能力;优异的抗湿性;低调的包装;......
  • 解决云电脑无法使用本地终端连接的USB设备
    本文分享自天翼云开发者社区《解决云电脑无法使用本地终端连接的USB设备》,作者:2****m云计算技术的广泛应用已经改变了我们对计算资源的使用方式。云电脑作为云计算的一个重要应用场景,提供了一种将计算资源从本地转移到云端的解决方案。通过云电脑,用户可以在任何地方使用云服务提......
  • 群晖nas 同步windows 设备笔记
    一.配置同步前的准备:https://kb.synology.cn/zh-cn/DSM/tutorial/Drive_Client_connection_issue确保SynologyNAS已开机。可以通过登录DSM或SynologyDrive网络门户来检查硬盘是否已开机。请确认您的网络连接稳定且工作正常。检查连接信息IP地址/域名/QuickConnectID 1......
  • Windows驱动中使用数字签名验证控制设备访问权限
    1.背景  在一般的驱动开发时,创建了符号链接后在应用层就可以访问打开我们的设备并进行通讯。  但我们有时候不希望非自己的进程访问我们的设备并进行交互,虽然可以使用IoCreateDeviceSecure来创建有安全描述符的设备,但大数的用户账户为了方便都是管理员,因此该方法不太完整......