字符设备驱动高级篇3——自动创建字符设备驱动的设备文件
以下内容源于朱有鹏《物联网大讲堂》课程的学习整理,如有侵权,请告知删除。
1、问题描述
- 使用mknod创建设备文件的缺点;
- 能否自动生成和删除设备文件;
2、解决方案:udev(嵌入式中用的是mdev),什么是udev?
- 应用层的一个应用程序;
- 内核驱动和应用层udev之间有一套信息传输机制(netlink协议);
- 应用层启用udev,内核驱动中使用相应接口;
- 驱动注册和注销时信息会被传给udev,由udev在应用层进行设备文件的创建和删除;
3、内核驱动设备类相关函数
- class_create
- device_create
4、编程实践
注册字符设备驱动完成后,添加设备类的操作,以让内核帮我们发信息!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#include <linux/module.h> // module_init module_exit
#include <linux/init.h> // __init __exit
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h> // arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#include <linux/string.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/cdev.h>
#include <linux/device.h>//#define MYMAJOR 200
#define MYCNT 1
#define MYNAME "testchar"#define GPJ0CON S5PV210_GPJ0CON
#define GPJ0DAT S5PV210_GPJ0DAT#define rGPJ0CON *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)#define GPJ0CON_PA 0xe0200240
#define GPJ0DAT_PA 0xe0200244unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;//int mymajor;
static dev_t mydev;
//static struct cdev test_cdev;
static struct cdev *pcdev;
static struct class *test_class;char kbuf[100]; // 内核空间的bufstatic int test_chrdev_open(struct inode *inode, struct file *file)
{// 这个函数中真正应该放置的是打开这个设备的硬件操作代码部分// 但是现在暂时我们写不了这么多,所以用一个printk打印个信息来做代表。printk(KERN_INFO "test_chrdev_open\n");rGPJ0CON = 0x11111111;rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); // 亮return 0;
}static int test_chrdev_release(struct inode *inode, struct file *file)
{printk(KERN_INFO "test_chrdev_release\n");rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));return 0;
}ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
{int ret = -1;printk(KERN_INFO "test_chrdev_read\n");ret = copy_to_user(ubuf, kbuf, count);if (ret){printk(KERN_ERR "copy_to_user fail\n");return -EINVAL;}printk(KERN_INFO "copy_to_user success..\n");return 0;
}// 写函数的本质就是将应用层传递过来的数据先复制到内核中,然后将之以正确的方式写入硬件完成操作。
static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf,size_t count, loff_t *ppos)
{int ret = -1;printk(KERN_INFO "test_chrdev_write\n");// 使用该函数将应用层传过来的ubuf中的内容拷贝到驱动空间中的一个buf中//memcpy(kbuf, ubuf); // 不行,因为2个不在一个地址空间中memset(kbuf, 0, sizeof(kbuf));ret = copy_from_user(kbuf, ubuf, count);if (ret){printk(KERN_ERR "copy_from_user fail\n");return -EINVAL;}printk(KERN_INFO "copy_from_user success..\n");if (kbuf[0] == '1'){rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));}else if (kbuf[0] == '0'){rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));}/*// 真正的驱动中,数据从应用层复制到驱动中后,我们就要根据这个数据// 去写硬件完成硬件的操作。所以这下面就应该是操作硬件的代码if (!strcmp(kbuf, "on")){rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));}else if (!strcmp(kbuf, "off")){rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));}
*/return 0;
}// 自定义一个file_operations结构体变量,并且去填充
static const struct file_operations test_fops = {.owner = THIS_MODULE, // 惯例,直接写即可.open = test_chrdev_open, // 将来应用open打开这个设备时实际调用的.release = test_chrdev_release, // 就是这个.open对应的函数.write = test_chrdev_write,.read = test_chrdev_read,
};// 模块安装函数
static int __init chrdev_init(void)
{ int retval;printk(KERN_INFO "chrdev_init helloworld init\n");// 使用新的cdev接口来注册字符设备驱动// 新的接口注册字符设备驱动需要2步// 第1步:分配主次设备号retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);if (retval < 0) {printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME);goto flag1;}printk(KERN_INFO "alloc_chrdev_region success\n");printk(KERN_INFO "major = %d, minor = %d.\n", MAJOR(mydev), MINOR(mydev));// 第2步:注册字符设备驱动pcdev = cdev_alloc(); // 给pcdev分配内存,指针实例化//cdev_init(pcdev, &test_fops);pcdev->owner = THIS_MODULE;pcdev->ops = &test_fops;retval = cdev_add(pcdev, mydev, MYCNT);if (retval) {printk(KERN_ERR "Unable to cdev_add\n");goto flag2;}printk(KERN_INFO "cdev_add success\n");// 注册字符设备驱动完成后,添加设备类的操作,以让内核帮我们发信息!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!// 给udev,让udev自动创建和删除设备文件test_class = class_create(THIS_MODULE, "aston_class");//类名if (IS_ERR(test_class))return -EINVAL;// 最后1个参数字符串,就是我们将来要在/dev目录下创建的设备文件的名字// 所以我们这里要的文件名是/dev/testdevice_create(test_class, NULL, mydev, NULL, "test");// 使用动态映射的方式来操作寄存器if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))
// return -EINVAL;goto flag3;if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))
// return -EINVAL;goto flag3;pGPJ0CON = ioremap(GPJ0CON_PA, 4);pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);*pGPJ0CON = 0x11111111;*pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); // 亮//goto flag0:return 0;// 如果第4步才出错跳转到这里来 release_mem_region(GPJ0CON_PA, 4);release_mem_region(GPJ0DAT_PA, 4);// 如果第3步才出错跳转到这里来
flag3:cdev_del(pcdev);// 如果第2步才出错跳转到这里来
flag2:// 在这里把第1步做成功的东西给注销掉unregister_chrdev_region(mydev, MYCNT);
// 如果第1步才出错跳转到这里来
flag1: return -EINVAL;
//flag0:
// return 0;
}// 模块下载函数
static void __exit chrdev_exit(void)
{printk(KERN_INFO "chrdev_exit helloworld exit\n");*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); // 解除映射iounmap(pGPJ0CON);iounmap(pGPJ0DAT);release_mem_region(GPJ0CON_PA, 4);release_mem_region(GPJ0DAT_PA, 4);/* // 在module_exit宏调用的函数中去注销字符设备驱动unregister_chrdev(mymajor, MYNAME);
*/ device_destroy(test_class, mydev);class_destroy(test_class);// 使用新的接口来注销字符设备驱动// 注销分2步:// 第一步真正注销字符设备驱动用cdev_delcdev_del(pcdev);// 第二步去注销申请的主次设备号unregister_chrdev_region(mydev, MYCNT);
}module_init(chrdev_init);
module_exit(chrdev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("aston"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息
字符设备驱动高级篇3——自动创建字符设备驱动的设备文件相关推荐
- Linux:驱动之自动创建字符设备的设备文件(未完)
自动创建字符设备的设备文件 目前尚不是最终版本,还望有心人自己学习的时候,把自己整合的知识点相关的答案也好问题也好,或者实践过程中的一些操作截图,再或者其他的一些想要分享材料发给笔者邮箱:uestc_ ...
- 字符设备驱动高级篇4——设备类(自动创建和删除设备文件)相关代码分析
以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 主要涉及class_create和device_create函数. 1.sys文件系统简介 (1)sys文件系统的 ...
- 字符设备驱动高级篇6——内核提供的读写寄存器接口
以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 1.前面访问寄存器的方式 通过定义指向寄存器的指针,然后解引用来对寄存器进行操作. (1)行不行?sure! (2 ...
- 字符设备驱动高级篇1——新接口介绍
以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 一.概览 1.新接口与老接口 (1)老接口:register_chrdev() 通过第一个参数是否为0,判断内核是 ...
- 龙芯2k按键中断驱动_字符设备驱动-高级篇按键中断程序驱动
驱动源码: #include "linux/module.h" #include"linux/kernel.h" #include"linux/fs. ...
- 字符设备驱动高级篇5——静态映射表、动态映射结构体方式操作寄存器
以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 一.静态映射表建立过程分析 1.建立映射表的三个关键部分 (1)映射表描述 具体物理地址和虚拟地址的值相关的宏定义 ...
- 字符设备驱动高级篇2——字符设备驱动注册代码分析
以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 1.老接口register_chrdev分析 (1)代码函数调用关系 register_chrdev __regi ...
- RK3399平台开发系列讲解(内核驱动外设篇)6.17、VOP驱动解析
平台 内核版本 安卓版本 RK3399 Linux4.4 Android7.1 文章目录 代码位置: 结构介绍: 寄存器读写: 图层接口: vop接口: 代码位置: kernel/drivers/gp ...
- Linux系统aboutyou,Linux字符设备驱动高级
设备号:主设备号 + 次设备号 也可以叫主次设备号 新接口注册字符设备驱动,其实就是填充这个struct cdev类型的结构体,主要填充的内容就是 file_operations这个结构体变量,让 ...
最新文章
- 3.推荐系统(矩阵分解)
- 【DataGuard】ORA-16014 and ORA-00312 Messages in Alert.log of Physical Standby
- Data Mining Tools
- android canvas_Android自定义View之绘制虚线
- 标机电脑上安装yarn
- supervisord的安装使用
- 超详细目标检测,yolo3训练自己的数据集
- word光标一直闪动_搞清楚啥是回车符,Word格式问题就解决了92.5%
- [react-native]react-native填坑笔记
- Oracle如何实现跨库查询
- 多维度积分管理系统java_Java毕业设计——超市积分管理系统项目设计
- 电脑、手机上那些让人用后欲罢不能的神器(附百度云链接)
- PHP 对接阿里云短信
- 【AI视野·今日CV 计算机视觉论文速览 第228期】Tue, 29 Jun 2021
- 虚拟现实(VR)技术的升级应用|时空克隆 三维视频融合 投影融合 点卯 魔镜系列
- [MOT学习笔记]JDE损失函数详解
- A-level 课程:最受欢迎和最不受欢迎的学科
- python网格搜索优化参数_python - 用于管道的网格搜索参数网格的说明 - SO中文参考 - www.soinside.com...
- 去除 csv 文件中的重复行
- ESP32使用freeRTOS的消息队列