三大字符设备注册方式详解

目录

1.简介 2

2.那么,对于杂项设备来说: 2

2.1注册函数:int misc_register(struct miscdevice *misc); 2

2.2注销函数:int misc_deregister(struct miscdevice *misc) 2

2.3特点: 3

3.那么,对于早期经典设备来说: 3

3.1注册函数:int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops) 3

3.2注销函数:static inline void unregister_chrdev(unsigned int major, const char *name) 3

3.3特点: 3

4.那么,对于linux2.6版本注册来说: 4

4.1首先,申请设备号: 4

4.2然后,添加设备: 5

4.3最后,自动创建设备节点: 5

4.4前面的一系列的操作完整的完成的设备驱动程序,然后就是注销 5

5.理解 6

 

 

 

 

 

 

 

 

 

 

 

 

1.简介

目的驱动:让硬件可以工作,驱动不会主动执行都是被调用然后做初始化工作让硬件可以工作

怎么办:为了让硬件在linux下运行,我们采用的办法就是将硬件注册为一个文件节点,供应用层读写操作

分类:字符设备;块设备;网络设备。

这里详解字符设备:

1.杂项设备注册

2.早期经典设备注册

3.Linux2.6设备注册

注:对于每一个设备来说,都经历3个过程:

1.设备号(主,次)的审请

2.注册添加设备(设备号与设备绑定)

3.创建设备节点

 

2.那么,对于杂项设备来说:

2.1注册函数:int misc_register(struct miscdevice *misc);

核心结构体:

struct miscdevice  {

int minor;//次设备号(0-254),取255系统自动分配

const char *name;//设备节点名,自动生成在/dev/下

const struct file_operations *fops;//文件操作集

struct list_head list;

struct device *parent;

struct device *this_device;

const char *nodename;

umode_t mode;

};

2.2注销函数:int misc_deregister(struct miscdevice *misc)

int misc_deregister(struct miscdevice *misc)

{

int i = DYNAMIC_MINORS - misc->minor - 1;

 

if (WARN_ON(list_empty(&misc->list)))

return -EINVAL;

 

mutex_lock(&misc_mtx);

list_del(&misc->list);

device_destroy(misc_class, MKDEV(10, misc->minor));

if (i < DYNAMIC_MINORS && i >= 0)

clear_bit(i, misc_minors);

mutex_unlock(&misc_mtx);

return 0;

可以看出device_destroy就是摧毁设备节点,那个节点取决于传的次设备号,因为主设备号已经确定,设备号和设备节点是对应的。

2.3特点:

1.主设备号指定为10,次设备号取值范围0-254;

2.一个注册函数完成所以步骤,即设备号的申请、设备号和设备的绑定、自动创建设备节点在/dev/下;

3.调用注册函数每次只能申请一个设备号,即我觉得在驱动不同种类的设备时用杂项设备注册发适用

3.那么,对于早期经典设备来说:

3.1注册函数:int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

major:主设备名0-255

name:设备名,而非设备节点名。在/proc/device下

fops:文件操作集

3.2注销函数:static inline void unregister_chrdev(unsigned int major, const char *name)

major:主设备号,需要打印出来,手动创建设备节点时候需要

name:设备名,而非设备节点名,和注册函数的参数一样

3.3特点:

1.主设备号取值范围为0-255,取0系统自动分配,一旦调用注册函数,会自动分配一个当前没有贝使用的主设备号,以及该主设备号下的所以次设备号

都被申请占用

2.一个注册函数完成了申请设备号、设备号与设备绑定,但是没有自动的创建设备节点,需要自己mknod /dev/xxx c 主设备号 次设备号

3. 没有使用一个结构体进行封装,没有做一个整体描述

4. 注销释放的是设备名而非设备节点,因为设备节点是自己手动创建的

 

4.那么,对于linux2.6版本注册来说:

两个核心结构体先摆在这里:

struct cdev {

     struct kobject kobj;

struct module *owner;//指定为THIS_MODULE,表示这个模块

const struct file_operations *ops;//设备节点管理的文件指针集

struct list_head list;//链表上下一个设备

dev_t dev;//完整设备号

unsigned int count;//申请设备号的数量

};

struct device *device_create(

struct class *cls, // 设备的类 类指针 让那个类来管理

struct device *parent, // NULL

dev_t devt, // 主设备号和次设备号的组合 32 位,主 12 位 次 20 位

void *drvdata, // NULL,设备私有数据

const char *fmt, … /*可以格式化的 fmt:/dev/下的节点名*/

);

申请设备号、注册添加设备(申请的设备号,和设备号绑定对应)、创建节点是分开做的,而非一个注册函数解决的。

 

4.1首先,申请设备号:

那么就需要将主设备号和次设备号整合成32位的,也需要从32位的完整设备号取出主设备号和次设备号,有相关的函数

MKDEV(ma,mi)//已知次设备号合成完整设备号

MpAJOR(dev)//从完整设备号提取主设备号

MINOR(dev)//从完整设备号提取次设备号

动态设备号申请:int alloc_chrdev_region(dev_t  *dev,  unsigned  baseminor,  unsigned count,  const  char *name)

dev:存放分配到的第一个设备(包括主次设备号)的指针,函数成功后会自动分配

baseminor:要分配起始次设备号(次设备号的起始值)

count:连续的次设备号数量

name:设备名,不需要和/dev的设备文件名相同

静态设备号的分配:int register_chrdev_region(dev_t from, unsigned count, const char *name)

from:自己指定的完整设备号

count:连续的次设备号数量

name:设备名,不需要和/dev的设备文件名相同

 

4.2然后,添加设备:

添加设备之前需要先核心结构初始化void cdev_init(struct cdev *cdev,const struct file_operations *fops)   

设备注册函数:int cdev_add(struct cdev *p,dev_t dev,unsigned count)  

参数p:已经初始化的核心结构指针

参数dev:起始设备号(包含主次设备号)

参数count:连续次设备号的数量

 

4.3最后,自动创建设备节点:

首先呢,需要创建class类:class_create(ower,name)

ower:类的所有者,我们一般会写THIS_MODULE代表这个模块

name:类的名字,自己随机取的

然后,创建设备节点:(struct device *device_create(struct class *class,struct device *parent,dev_t devt,void *drvdata,const char *fmt,.....))

class:上面创建类的返回值,是个指针

parent:父设备,一般没有就写NULL

devt:完整的设备名

drvdata:驱动数据,也可以是NULL,一般为NULL

fmt,...可变参数,用来生成/dev/目录下的设备文件名

函数成功返回一个指向节点描述结构体的指针

 

4.4前面的一系列的操作完整的完成的设备驱动程序,然后就是注销

 

1.摧毁设备节点 void device_destroy(struct class *class,dev_t  devt)

2.摧毁类 void class_destroy(struct class *cls)

3注销cdev结构空间 void cdev_del(struct cdev *p)   

4释放设备号 void unregister_chrdev_region(dev_t from,  unsigned  int count)

这个注销的顺序不能随意

 

5.理解

那么我们该如果选择三种注册方式呢

其实,看上去都可以,但是如果选择不当会很复杂,比如说,我想同种类的设备,像多个usb,用早期经典注册方式和linux2.6注册方式就比较利落些因为它可以在同一个主设备下开多个次设备,这样我们就可以创建多个设备节点使用同一个设备

疑问,设备节点,驱动,硬件设备是如何关联到一起的呢?

百度后得知:这是通过设备号实现的,包括主设备号和次设备号。当我们创建一个设备节点时需要指定主设备号和次设备号。

应用程序通过名称访问设备,而设备号指定了对应的驱动程序和对应的设备。主设备号标识设备对应的驱动程序,次设备号

由内核使用,用于确定设备节点所指设备。

什么叫做设备节点?

连接内核与用户层的枢纽,是设备是接到对应哪种接口的哪个ID 上

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值