[ARM-Linux开发] 主设备号--驱动模块与设备节点联系的纽带
一、如何对设备操作
linux中对设备进行操作是通过文件的方式进行的,包括open、read、write。
对于设备文件,一般称其为设备节点,
节点有一个属性是设备号(主设备号、次设备号),其中主设备号将设备文件与驱动模块对应起来
当我们open一个设备节点时,告诉了kernel要操作的是是主设备号为XX的节点,然后kernel会通过过XX来寻找合适的内存模块,进而调用内存模块中定义的open函数
由于操作节点之前kernel就需要有主设备号的信息,因此主设备号的申请、具有该主设备号的字符设备的添加都需要在驱动模块的初始化函数中执行
二、主设备号的申请
建议采用动态申请的主设备号的方式,linux中有很多设备,每一个设备对应着一个主设备号,动态申请是由内核分配一个没用的主
设备号,
动态申请函数为alloc_chrdev_region,相对应的释放函数为unregister_chrdev_region。
申请完后,可以从/proc/devices中读到分配的主设备号,后面建立设备节点时还需要用到
三、向kernle添加字符设备
上一步向内核申请了主设备号,就可以向kernel中添加字符设备了
kennel中一个字符设备对应了一个结构体cdev,这个结构体中定义了对字符设备的操作方式file_operations(包括open、read、write),这些操作方式也需要在驱动模块中事先定义好。
字符设备结构体cdev的添加步骤:
cdev初始化:cdev_init,该函数将file_operations与cdev对应起来
向kernel添加:cdev_add,该函数将主设备号与cdev结构体对应起来
当对open设备节点时,首先通过节点找到主设备号,然后再kernel中搜索与主设备号相对应的字符设备cdev,然后动过cdev中file_operations结构体定义的open方法(这个open是需要自己实现的)
四、3个重要的结构体
一个是file_operations,这里面主要包含了驱动的主要实现方法
一个是inode,这个是节点的信息,包含了主设备号和cdev结构体
一个是file,当节点首次被打开时,就会在内核中创建一个file结构体,file结构其充当了file_operations中方法的纽带,要不然read和wirte方法怎么知道操作的是那个设备的数据。
file中的自定义内容(驱动需要的数据)一般是在open中定义,然后read和write就可以操作自定义的数据了。
下面是一个简单的实例,可以看到驱动是怎样把自定义的open方法和主设备号对应起来的
#include <linux/module.h> /*它定义了模块的 API、类型和宏(MODULE_LICENSE、MODULE_AUTHOR等等),所有的内核模块都必须包含这个头文件。*/
#include <linux/init.h>
#include <linux/fs.h> //设备号相关函数
#include <linux/slab.h> //内存分配相关函数
#include <linux/types.h>
#include <linux/kdev_t.h>//设备号相关函数
#include <linux/cdev.h>//字符设备头文件
#include <linux/module.h>
struct char_dev
{
int size;
char *data;
struct cdev cdev;//内核中的字符设备
};
int major = 0;
int minor = 0;
struct char_dev char_devices;
int char_open(struct inode *inode, struct file *filep)
{
int Major = 0;
Major = MAJOR(inode->i_rdev);
printk("open my_char_dev major: %d\n", Major);
return 0;
}
struct file_operations char_fops = {
.owner = THIS_MODULE,
.open = char_open,
};
static void char_exit(void) //如果init函数中调用了该函数,则不应有 __exit
{
dev_t dev;
printk("char device driver exit \n");
//释放设备号
dev = MKDEV(major, minor);
unregister_chrdev_region(dev, 1);
printk("release major %d\n", major);
//释放内存
if(char_devices.data){
kfree(char_devices.data);
}
//从内核中删除字符设备
cdev_del(&(char_devices.cdev));
}
static int __init char_init(void)//__init一个标记,表明是初始化函数
{
//初始化的代码
dev_t dev;
int result;
printk("char device driver init \n");
//动态向内核申请设备号
result = alloc_chrdev_region(&dev, 0, 1, "my_char_dev");
major = MAJOR(dev);
minor = MINOR(dev);
printk("alloc major %d\n", major);
if (result < 0) {
printk(KERN_WARNING "my_char_dev: can't get major %d\n", major);
return result;
}
//为设备分配一块内存
char_devices.size = 100;
char_devices.data = (char*)kmalloc(char_devices.size, GFP_KERNEL);
if (!char_devices.data) {
result = -ENOMEM;
goto fail; //不能直接退出函数,需要释放设备号
}
//向内核中添加字符设备cdev
cdev_init(&(char_devices.cdev), &char_fops);
char_devices.cdev.owner = THIS_MODULE;
char_devices.cdev.ops = &char_fops;
result = cdev_add(&(char_devices.cdev), dev, 1);
if((result < 0)) {
printk(KERN_WARNING "Error %d adding my_char_dev\n", result);
goto fail;
}
return 0; //成功
fail:
char_exit();
return result;
}
MODULE_LICENSE("Dual BSD/GPL");
//当模块被加载时,执行moudle_init函数,该函数会调用初始化函数
module_init(char_init);
//模块卸载时,调用,释放资源
module_exit(char_exit);
KDIR=/usr/src/linux-headers-$(shell uname -r)
PWD=$(shell pwd)
obj-m = CharDevice.o
all:
$(MAKE) -C $(KDIR) M=$(PWD)
注:驱动insmod后,通过/proc/devices查看主设备号,然后通过mknod在/dev下创建设备节点,注意保持主设备好的一致,当前的节点只支持open方法,可以在demsg中进行验证。
[ARM-Linux开发] 主设备号--驱动模块与设备节点联系的纽带的更多相关文章
- linux设备管理之主设备号与次设备号
主设备号和次设备号 一个字符设备或者块设备都有一个主设备号和次设备号.主设备号和次设备号统称为设备号.主设备号用来表示一个特定的驱动程序.次设备号用来表示使用该驱动程序的其他设备.(主设备号和控制这类 ...
- Linux:主设备号和次设备号
http://www.linuxidc.com/Linux/2011-03/33863.htm Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备 ...
- Linux chroot 并使用之前系统设备节点
/********************************************************************************* * Linux chroot 并使 ...
- 在Ubuntu上建立Arm Linux 开发环境
我使用的是友善2410的板子,以前都是用Fedora,现在家里的电脑被我转为Linux专用的了,装的是Ubuntu.但是嵌入式还是要玩的,在装载过程中也遇到一些小麻烦.在此记录一下,一来自己比较健忘, ...
- linux驱动之设备号与创建设备节点
设备号: 1.自己主动分配 major = register_chrdev(0,"first_drv",&first_sdv_fops);//注冊 注冊设备时给设备号写0, ...
- 成功移植SQLite3到ARM Linux开发板
SQLite,是一款轻型的数据库,是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了 ...
- minigui移植到arm linux开发板上无法执行
要保证目录下有该文件 /etc/MiniGUI.cfg 复制过程使用cp –af 强制复制
- 嵌入式Linux学习笔记(三) 字符型设备驱动--LED的驱动开发
在成功构建了一个能够运行在开发板平台的系统后,下一步就要正式开始应用的开发(这里前提是有一定的C语言基础,对ARM体系的软/硬件,这部分有疑问可能要参考其它教程),根据需求仔细分解任务,可以发现包含的 ...
- 【Linux开发】linux设备驱动归纳总结(三):1.字符型设备之设备申请
linux设备驱动归纳总结(三):1.字符型设备之设备申请 操作系统:Ubunru 10.04 实验平台:S3C2440 + linux2.6.29内核 注:在今后驱动程序的学习中经常需要查看内核源代 ...
随机推荐
- test20191020 往复
往复 Coldhac 做不出题了,他在长为 n 的走廊里走来走去.从位置 1 开始, 每次他会向前走长为 i ∈ [1, k] 的一步(不能超出走廊的范围),直至到达位 置 n. 在想出正解前,Col ...
- Linux:使用awk命令获取文本的某一行,某一列;sed插入指定的内容到指定文件中
awk相关用法: 1.打印文件的第一列(域) : awk '{print $1}' filename2.打印文件的前两列(域) : aw ...
- Servlet 容器
Servlet容器主要是JavaWeb应用提供运行时环境,所以也可以称之为JavaWeb应用容器,或者Servlet/JSP容器.Servlet容器主要负责管理Servlet.JSP的生命周期以及它们 ...
- 彻底掌握网络通信(七)ConnectionReuseStrategy,ConnectionKeepAliveStrategy解析
网络通信系列文章序 彻底掌握网络通信(一)Http协议基础知识 彻底掌握网络通信(二)Apache的HttpClient基础知识 彻底掌握网络通信(三)Android源码中HttpClient的在不同 ...
- LeetCode 499. The Maze III
原题链接在这里:https://leetcode.com/problems/the-maze-iii/ 题目: There is a ball in a maze with empty spaces ...
- sql 记录一次灾难 游标问题
起因:游标执行存储过程 下载begin 外面了.. ,造成一直触发存储过程 收获:定义变量统一在游标外部使用, 书写内容在begin 内部书写 alter PROCEDURE USP_dgd_wzh_ ...
- 8.学习springmvc的拦截器
一.springmvc拦截器介绍和环境搭建 1.介绍: 过滤器:servlet中的一部分,可以拦截所有想要访问的资源. 拦截器:SpringMVC框架中的,只能在SpringMVC中使用并且只能过滤控 ...
- poj2398 Toy Storage 计算几何,叉积,二分
poj2398 Toy Storage 链接 poj 题目大意 这道题的大概意思是先输入6个数字:n,m,x1,y1,x2,y2.n代表卡片的数量,卡片竖直(或倾斜)放置在盒内,可把盒子分为n+1块区 ...
- 2019 SDN课程阅读作业(2)
1.过去20年中可编程网络的发展可以分为几个阶段?每个阶段的贡献是什么? 主动网络(从1990年代中期到2000年代初) 它在网络中引入了可编程的功能以实现更多的创新: 20世纪90年代初,主动网络研 ...
- MYSQL避免重复插入记录的三种方法
方案一:使用ignore关键字 如果是用主键primary或者唯一索引unique区分了记录的唯一性,避免重复插入记录可以使用: insert ignore into table_name(ema ...