字符设备驱动高级篇3——自动创建设备文件

以下内容源于朱有鹏嵌入式课程学习与整理,如有侵权请告知删除。

问题引入

之前在应用层测试驱动源程序时,需要先安装驱动模块,安装驱动模块后会得到一个主设备号,然后在命令行利用mknod命令“mknod /dev/xxx c 主设备号 次设备号”来创建设备文件。也就是说,设备文件需要我们手动创建。

那能不能自动生成设备文件呢?

问题解答

1、udev简介

可以利用udev来自动生成设备文件(嵌入式中用的是mdev)。

udev是应用层的一个应用程序,驱动和应用层udev之间有一套信息传输机制(netlink协议)。应用层启用udev,而且驱动使用相应接口后,驱动注册和注销时信息会被传给udev,udev会在应用层进行设备文件的创建和删除。

2、驱动相关接口(设备类操作接口)

class_create()函数         //用于创建设备分类的目录,即sys/class/xxx

device_create()函数       //用于创建设备文件,即/dev/xxx

device_destroy()函数

class_destroy()函数

测试

将附录的代码进行编译后,在开发板上对比安装前后的情况。

[root@xjh class]# pwd
/sys/class[root@xjh class]# ls          //此时还没有安装驱动模块,所以没有xjh_class 这个类别
backlight     hidraw        mem           ppp           scsi_device switch
bdi           i2c-adapter   misc          pvr           scsi_disk   timed_output
block         i2c-dev       mmc_host      regulator     scsi_generic tty
firmware      ieee80211     mtd           rfkill        scsi_host    vc
gpio          input         net           rtc           sound       video4linux
graphics      lcd           power_supply  s3c_bc        spi_master  vtconsole[root@xjh class]# insmod /mnt/module_test.ko  //安装驱动模块
[  109.978015] chrdev_init helloworld init
[  109.981226] alloc_chrdev_region success
[  109.984211] major = 250, minor = 12.
[  109.987772] cdev_add success[root@xjh class]# ls           //安装驱动模块之后,出现xjh_class
backlight     i2c-adapter   mmc_host      rfkill   sound        vtconsole
bdi           i2c-dev       mtd           rtc      spi_master   xjh_class//出现
//省略部分显示[root@xjh class]# cd xjh_class/ //xjh_class中有一个以设备文件名(test)作为名字的文件夹
[root@xjh xjh_class]# ls        //注意不是以设备驱动名testchar来命名
test                                       
[root@xjh xjh_class]# cd test/  
[root@xjh test]# ls             //以设备文件名命名的文件夹,其内容类似如下
dev        power      subsystem  uevent
[root@xjh test]# cat dev        //这个和安装驱动模块时的输出信息是一致的。
250:12  [root@xjh test]# cd /dev
[root@xjh dev]# ls  //此时出现设备文件test,注意这里是文件而非目录
CEC                 ptyr6               sequencer2          ttyq4
HPD                 ptyr7               snd                 ttyq5
adc                 ptyr8               test//出现设备文件   ttyq6
//省略部分显示
[root@xjh dev]# ls -l test  //单独显示设备文件test的信息
crw-rw----    1 root     root      250,  12 Jan  1 12:01 test[root@xjh dev]# cat /proc/devices 
Character devices:1 mem2 pty
//省略部分显示
250 testchar                //这里出现了主设备号为250的设备,设备名叫testchar
//省略部分显示//注意设备号,设备文件(名),设备名(或者叫驱动名),设备所属类别名
// 250,12    /dev/test        testchar              xjh_class

附录代码

#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(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));}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, "xjh_class");//该函数将创建sys/class/xjh_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");			// 描述模块的别名信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461280.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

long 转为string_面试必问 Redis数据结构底层原理String、List篇

点击关注上方“Java大厂面试官”&#xff0c;第一时间送达技术干货。阅读文本大概需要 8 分钟。前言今天来整理学习下Redis有哪些常用数据结构&#xff0c;都是怎么使用的呢&#xff1f;首先看下全局存储结构。全局存储结构基础你们肯定都知道&#xff0c;redis支持的基础数据结…

wpf 3D学习

最近在看一些关于wpf 3d的效果&#xff0c;研究了一些代码特效&#xff0c;现在和广大博友共享一下. 首先用到的是MeshGeometry3D&#xff0c;msdn上介绍&#xff1a;用于生成三维形状的三角形基元。主要有4个依赖属性&#xff1a;NormalsProperty&#xff0c;PositionsPropert…

字符设备驱动高级篇4——自动创建设备文件的函数代码分析

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、概述 设备文件的创建&#xff0c;主要涉及class_create()函数、device_create()函数。 class_create()函数用于自动创建 /sys/class/目录下的xxx目录。 device_create()函数用于自动创建 /dev/…

unicode字符、python乱码问题

http://www.cnblogs.com/BeginMan/archive/2013/08/08/3246619.html#a1 Python常见常用知识点http://blog.csdn.net/tingsking18/article/details/4033645 Unicode和Python的中文处理如何让Python的Unicode字符串支持中文&#xff1f;要想利用Python的Unicode机制处理字符串&…

win10下如何安装vb6.0sp6_Mac如何安装win10系统?Parallels Desktop 15 Mac安装win10系统教程...

Parallels Desktop 15 mac版是mac上非常强大也非常好用的虚拟机软件&#xff0c;最新版本的parallels desktop mac 15针对最新的Windows 10更新和macOS Catalina&#xff08;10.15&#xff09;进行了优化。今天分享的内容就是Parallels Desktop 15 mac版如何安装win10系统。PD虚…

android面试题精选

1.android dvm 的进程和Linux的进程&#xff0c;应用程序的进程是否为同一个概念&#xff1a; 答&#xff1a;dvm是dalivk虚拟机。每一个android应用程序都在自己的进程中运行&#xff0c;都拥有一个dalivk虚拟机实例。而每一个dvm都是在linux的一个进程。所以说可以认为是同一…

字符设备驱动高级篇5——静态映射表的建立过程,动态映射结构体方式操作寄存器

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 补充内容&#xff1a;字符设备驱动基础5——驱动如何操控硬件_天糊土的博客-CSDN博客 一、静态映射表的建立过程 关于“静态映射表的建立”这部分内容&#xff0c;有以下三个关键&#xff1a; &…

python 分布图_python数据分布型图表柱形分布图系列带误差线的柱形图

柱形分布图系列柱形分布图系列使用柱形图的方式展示数据的分布规律&#xff1b;可以借助误差线或散点图&#xff1b;带误差线的柱形图就是使用每个类别的均值作为柱形的高度&#xff1b;再根据每个类别的标准差绘制误差线&#xff1b;缺点&#xff1a;无法显示数据的分布情况&a…

[汇编] 002基础知识-CPU和寄存器

CPU是什么 当然这里的内存不仅仅指电脑上的内存&#xff0c;例如&#xff1a;我的金士顿8G内存&#xff0c;七彩虹1G独显&#xff0c;在这里来说&#xff0c;显卡也是有内存的(寄存器) CPU如何控制其它部件的&#xff1f; 问题&#xff1a;CPU是如何和电脑主机中其它芯片有条不…

Asp.net中页面传值几种方式

页面传值是学习asp.net初期都会面临的一个问题&#xff0c;总的来说有页面传值、存储对象传值、ajax、类、model、表单等。但是一般来说&#xff0c;常用的较简单有QueryString&#xff0c;Session&#xff0c;Cookies&#xff0c;Application&#xff0c;Server.Transfer。  …

字符设备驱动高级篇6——内核提供的读写寄存器接口

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 1、访问寄存器的方式 之前对寄存器的操作&#xff0c;都是先定义指向寄存器的指针&#xff0c;然后再解引用来对寄存器进行操作。这是因为ARM体系中&#xff0c;内存和IO是统一编址的。但是其他体系…

java台球游戏设计原理_Java实现简单台球游戏

Java实现简单台球桌问题&#xff0c;供大家参考&#xff0c;具体内容如下需求&#xff1a;使小球可以在桌面上移动&#xff0c;移动到桌面边缘将被弹回&#xff0c;显示小区的移动素材&#xff1a;小球照片桌球照片程序源代码&#xff1a;package 桌球游戏;import java.awt.*;i…

wordpress教程:默认http头信息X-Pingback的隐藏与修改

利用站长工具的http状态查询工具查询可以看到类似如下的一段http HEAD信息 X-Pingback: http://www.kristain.com/wordpress真实路径/xmlrpc.php 其实这样就已经暴露了wordpress网站的真实路径了&#xff0c;那么如何来隐藏wordpress默认http HEAD信息中的X-Pingback信息呢&…

关于java assertion

大部分转载自参考资料&#xff1a;http://www.ibm.com/developerworks/cn/java/l-javaassertion/index.html assertion(断言)在软件开发中是一种常用的调试方式&#xff0c;assertion就是在程序中的一条语句&#xff0c;它对一个boolean表达式进行检查&#xff0c;一个正确程序…

mupdf不支持x64_Delphi xe2使用x64编译器编译ASM代码时出错 . 不支持的语言功能:'ASM'...

代码无法直接正确移植到x64&#xff0c;因为它将执行64位指针截断 - 有关详细信息&#xff0c;请参见下文 .64位应用程序不支持将汇编语句与Pascal代码混合使用 . 使用Pascal代码或完全用汇编编写的函数替换汇编语句 .这里使用装配是不必要的 . 我不确定为什么原作者会选择去解…

IOC是什么?

2019独角兽企业重金招聘Python工程师标准>>> Inversion of Control&#xff0c;即反转控制&#xff0c;或许说为依赖注入更为合适。IoC就是一种设计模式。 Interface Driven Design接口驱动&#xff0c;接口驱动有很多好处&#xff0c;可以提供不同灵活的子类实现&a…

poj2516Minimum Cost

http://poj.org/problem?id2516 建图的时候 有个地方写错了 卡了半年。。 题意看了N久啊 有N个店主需要K种物品 有M个供应点 每个供应点有K种物品 其实是算K次最小费用 然后叠加 分解开来这题就是求把某种物品从供应点送到店主那里 多个源点-》多个汇点 所以加一个超级源点 和…

设备驱动框架1——LED驱动框架的分析(核心层)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、驱动框架的含义 1、理解层面1&#xff1a;驱动的分层设计 设备驱动程序&#xff0c;是由内核驱动部分的维护者&#xff0c;以及驱动开发工程师协作完成的。 内核驱动部分的维护者&#xff0c;往…

myeclipse连接mysql怎么调用_myeclipse连接mysql数据库详细步骤

第一步 打开Database windows-prefenrence-showview-DBbrowser ,此时会在工具底部有个DBbrowser &#xff0c;选中它&#xff0c;再它所控制的页面的任意位置 右击new---跳转到一个配置driver的页面 (选择连接方式)图一打开myeclipse然后点击window窗口 点击Open Perspective…

struts2中文件上传

注意点 private File image;//对应的就是表单中文件上传的那个输入域的名称&#xff0c;Struts2框架会封装成File类型的private String imageFileName;// 上传输入域FileName 文件名private String imageContentType;// 上传文件的MIME类型 单个文件 1 package cn.itcast.ac…