Linux教程

linux字符设备驱动开发

本文主要是介绍linux字符设备驱动开发,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

linux字符设备驱动开发

从驱动框架图可以看出,在Linux操作系统中,用户要想使用外接硬件设备,是通过调用库函数获取设备文件,通过设备文件调用驱动,驱使硬件设备完成工作。

设备驱动框架图

在这里插入图片描述


字符设备、字符设备驱动与用户空间访问该设备的程序三者之间的关系

在这里插入图片描述

字符设备驱动模型

在这里插入图片描述

文章目录

  • linux字符设备驱动开发
  • 设备驱动框架图
  • 字符设备、字符设备驱动与用户空间访问该设备的程序三者之间的关系
  • 字符设备驱动模型
  • 前言
  • 一、Linux内核驱动接口介绍
    • 1、注册字符设备驱动
      • 1.1、register_chrdev
      • 1.2、register_chrdev_region
      • 1.3、 alloc_chrdev_region
      • 1.4、unregister_chrdev_region
  • 二、Struct cdev 结构体的定义及操作
      • 2.1、 void cdev_init(struct cdev *cdev, const struct file_operations *fops)
      • 2.2、struct cdev *cdev_alloc(void)
      • 2.3、int cdev_add(struct cdev *p, dev_t dev, unsigned count);
      • 2.4、void cdev_del(struct cdev *p);
  • 三、
  • Demo


前言


一、Linux内核驱动接口介绍

1、注册字符设备驱动

1.1、register_chrdev

register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
(1)确定一个主设备号,如果major=0,则会自动分配设备号
(2)构造一个file_operations结构体, 然后放在chrdevs数组中
(3)注册:register_chrdev,cat /proc/devices查看内核中已经注册过的字符设备驱动(和块设备驱动),注意这里并不是驱动文件设备节点!

然后当读写字符设备的时候,就会根据主设备号从chrdevs数组中取出相应的结构体,并调用相应的处理函数
每注册一个字符设备,还会连续注册0~255个次设备号,使它们绑定在同一个file_operations操作方法结构体上,在大多数情况下,都只用极少的次设备号,所以会浪费很多资源。在 2.4 的内核我们使用 register_chrdev(0, "hello", &hello_fops) 来进行字符设备设备节点的分配,这种方式每一个主设备号只能存放一种设备,它们使用相同的 file_operation 结构体,也就是说内核最多支持 256 个字符设备驱动程序。

之前写的字符类设备驱动,没有自动创建设备节点,因为只使用了register_chrdev()函数,只是注册了这个设备。然后在系统启动后,就要自己创建设备节点mknod,这样虽然是可行的,但是比较麻烦。于是想在__init函数里面,自动创建设备节点。

1.2、register_chrdev_region

//指定设备编号来静态注册一个字符设备
int register_chrdev_region(dev_t from, unsigned count, const char *name);  

from: 注册的指定起始设备编号,比如:MKDEV(100, 0),表示起始主设备号100, 起始次设备号为0

count:需要连续注册的次设备编号个数,比如: 起始次设备号为0,count=100,表示0~99的次设备号都要绑定在同一个file_operations操作方法结构体上

name:字符设备名称

当返回值小于0,表示注册失败

1.3、 alloc_chrdev_region

/*动态分配一个字符设备,注册成功并将分配到的主次设备号放入*dev里*/

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);

*dev: 存放起始设备编号的指针,当注册成功, *dev就会等于分配到的起始设备编号,可以通过MAJOR()和MINNOR()函数来提取主次设备号

baseminor:次设备号基地址,也就是起始次设备号

count:需要连续注册的次设备编号个数,比如: 起始次设备号(baseminor)为0,baseminor=2,表示0~1的此设备号都要绑定在同一个file_operations操作方法结构体上

*name:字符设备名称

当返回值小于0,表示注册失败

1.4、unregister_chrdev_region

/*注销字符设备*/

void unregister_chrdev_region(dev_t from, unsigned count);

from: 注销的指定起始设备编号,比如:MKDEV(100, 0),表示起始主设备号100, 起始次设备号为0

count:需要连续注销的次设备编号个数,比如: 起始次设备号为0,baseminor=100,表示注销掉0~99的次设备号

3.接下来,我们便来写一个字符设备驱动

里面调用两次上面的函数,构造两个不同的file_operations操作结构体,

次设备号0~1对应第一个file_operations,

次设备号2~3对应第二个file_operations,

然后在/dev/下,通过次设备号(0~4)创建5个设备节点, 利用应用程序打开这5个文件,看有什么现象

二、Struct cdev 结构体的定义及操作

1. 在Linux内核中,使用cdev结构体来描述一个字符设备,cdev结构体的定义如下:

<include/linux/cdev.h>
 
struct cdev { 
	struct kobject kobj;                  //内嵌的内核对象.
	struct module *owner;                 //该字符设备所在的内核模块的对象指针.
	const struct file_operations *ops;    //该结构描述了字符设备所能实现的方法,
	struct list_head list;                //用来将已经向内核注册的所有字符设备形成链表.
	dev_t dev;                            //字符设备的设备号,由主设备号和次设备号构成.
	unsigned int count;                   //隶属于同一主设备号的次设备号的个数.
};

2. 内核给出的操作struct cdev结构的接口主要有以下几个:

2.1、 void cdev_init(struct cdev *cdev, const struct file_operations *fops)

//对struct cdev结构体做初始化, 建立cdev 和 file_operations之间的连接:
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
	memset(cdev, 0, sizeof *cdev); //将整个结构体清零
	INIT_LIST_HEAD(&cdev->list);	//初始化list成员使其指向自身
	kobject_init(&cdev->kobj, &ktype_cdev_default); //初始化kobj成员
	cdev->ops = fops; // 初始化ops成员;
}

2.2、struct cdev *cdev_alloc(void)

//分配一个struct cdev结构,动态申请一个cdev内存
struct cdev *cdev_alloc(void)
{
	struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
	if (p) {
		INIT_LIST_HEAD(&p->list);
		kobject_init(&p->kobj, &ktype_cdev_dynamic);
	}
	return p;

2.3、int cdev_add(struct cdev *p, dev_t dev, unsigned count);

//内核注册一个struct cdev结构体,struct cdev *p代表的字符设备就可以使用了
int cdev_add(struct cdev *p, dev_t dev, unsigned count);

 1. p:字符设备指针
 2. dev:设备号,赋值给struct cdev的dev成员。
 3. count:设备关联的设备编号的数量,赋值给struct cdev的count成员。

2.4、void cdev_del(struct cdev *p);

//向内核注销一个struct cdev结构,struct cdev *p代表的字符设备就不可以使用了
void cdev_del(struct cdev *p);

三、



Demo

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

这篇关于linux字符设备驱动开发的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!