字符设备驱动详解(主次设备号、注册/卸载字符设备驱动、创建设备节点、地址映射)

1、主次设备号

(1)主次设备号是内核用来索引设备的,每个主次设备号在内核中都是唯一的,每个注册的设备都有一个分配的主次设备号;
(2)同一个主设备号可以有多个从设备号,主设备是对应的驱动程序,次设备号对应设备文件所指的设备。一个Soc可能接同样功能
的几个设备,这些设备属于同类设备甚至可以共享同一个驱动;那这几个设备就共用一个主设备号,然后再用次设备号来区分;
(3)多个次设备号共享主设备号是为了解决主设备号过多的问题,并且同类设备共享一个主设备号也便于理解和管理,但是仍有很多设备还是按照“一个主设备号对应一个设备”的原则组织;

2、查看设备的主次设备号

2.1、驱动已经注册并且创建了设备节点

在这里插入图片描述

(1)对设备的访问是通过文件系统内的设备名称进行的,这些文件被称为特殊文件、设备文件,通常位于/dev目录下;
(2)"c"开头的是字符设备,"b"开头的是块设备;
(3)红框里的就是主次设备号,可以看到主设备号218就对应多个次设备号;

2.2、驱动已经注册但还没有创建设备节点

root@ubuntu: cat /proc/devices 
Character devices:
 ······
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
216 rfcomm
226 drm
251 hidraw
252 bsg
253 watchdog
254 rtc

Block devices:
  1 ramdisk
  2 fd
259 blkext
  7 loop
  8 sd
  9 md
······
252 device-mapper
253 virtblk
254 mdp
root@ubuntu:~/dai_zhi_xin/kernel/jiuding_kernel/kernel# 

cat /proc/devices :可以查看已经注册的驱动程序,但是只显示主设备号;

3、设备编号的内部表达

typedef unsigned int __u32;
typedef __u32 __kernel_dev_t;
typedef __kernel_dev_t		dev_t;

//从设备编号中获取主设备号
MAJOR(dev_t dev);

//从设备号中获取次设备号
MINOR(dev_t dev);

//根据主次设备号构建出dev_t类型的设备编号
MKDEV(int major, int minor);

(1)在内核中用dev_t类型表示设备编号,其实dev_t就是unsigned int类型,只不过其中的某几位表示主设备号,剩下的位表示次设备号;
(2)不同版本的内核,dev_t中用来表示主设备号和次设备号的位数不同,为了兼容性我们不能在代码中直接写死,要用上面的宏定义来操作设备编号;
补充:2.6内核之前的版本限定最大255个主设备和255个次设备号;

4、分配设备编号

4.1、指定设备号和动态分配设备号

(1)指定设备号:当我们知道当前系统未被占用的设备编号,可以在申请设备号时指定一个空闲的设备编号。劣势:当我们写的驱动程序被别人移植时,因为别人的系统
环境和我们不同,驱动代码里指定的设备编号很可能已经被占用,这样就会报错;优势:因为知道设备编号,可以预先创建设备节点;
(2)动态分配编号:不指定设备编号,而是使用系统分配的空闲的设备标号。劣势:不同的系统,分配的设备编号不同,所以不能预先创建设备节点;
优势:驱动代码可移植性强,不用关心当前系统哪些设备号是空闲的;
总结:强烈建议使用动态分配设备编号,然后用加载脚本或者udev去解决设备节点的创建问题;在内核的设备号申请函数中,默认使用动态分配,但也保留的指定设备号的申请接口;

4.2、指定设备号

int register_chrdev_region(dev_t first, unsigned count, const char *name);

(1)first:要分配的设备编号范围的起始值,first的次设备号经常被置为零,但不是必须的;
(2)count:请求的连续设备编号的个数;
(3)name:和编号范围关联的设备名称,将出现在/proc/devices和sysfs中;
总结:这个函数要提前明确知道所需要的设备编号,如果你申请分配的主设备已经被占用则会出错;

4.3、动态分配设备号

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

(1)dev:这是用于输出的参数,内核会将已分配范围的第一个编号返回回来;
(2)firstminor:申请的第一个次设备号,通常是0。如果你不填0,假如你填3,则次设备号就是从3开始,依次往后增长;
(3)count:就是你要申请多少个设备号,也就是次设备号有多少个;
(4)name:和编号范围关联的设备名称,将出现在/proc/devices和sysfs中;

5、释放设备号

void unregister_chrdev_region(dev_t first, unsigned int count);

(1)first:要释放的起始设备编号;
(2)count:释放多少个设备编号,因为一个主设备号对应多个次设备号;

6、注册字符设备驱动

#define MYCNT		1
#define MYNAME		"testchar"

struct cdev *pcdev;
dev_t mydev

// 自定义一个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,
};


//动态申请主次设备号
alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);

//给pcdev分配内存,指针实例化
pcdev = cdev_alloc(); 

//填充pcdev指向的结构体,有两种方式,调用cdev_init函数或者直接赋值
// cdev_init(pcdev, &test_fops);
pcdev->owner = THIS_MODULE;
pcdev->ops = &test_fops;

//注册字符设备驱动
cdev_add(pcdev, mydev, MYCNT);

(1)申请主次设备号;
(2)申请struct cdev结构体内存;
(3)填充struct cdev结构体,主要是赋值pcdev->ops,这是驱动程序的操作方法;

7、卸载字符设备驱动

//void cdev_del(struct cdev *p)
cdev_del(pcdev);

8、struct inode & struct file & struct file_operations

(1)struct inode:每个硬盘上的文件都有一个struct inode结构体来表示;
(2)struct file:每个打开的文件都对应一个struct file结构体;
(3)struct file_operations:字符驱动程序的操作方法;比如:open、read、write等操作如何操作字符设备;
参考博客:《驱动中重要的三个结构体介绍:struct inode、struct file、struct file_operations》

copy_to_user & copy_from_user

内核和用户空间之间的数据拷贝;参考博客:《内核空间和应用空间的数据拷贝(copy_to_user & copy_from_user)》

9、老版本的字符设备注册接口和卸载接口

//注册接口
static inline int register_chrdev(unsigned int major, const char *name,
				  const struct file_operations *fops)
				  
// 卸载接口 
static inline void unregister_chrdev(unsigned int major, const char *name)

(1)major:major传0进去表示要让内核帮我们自动分配一个合适的空白的没被使用的主设备号,内核如果成功分配就会返回分配的主设备好,如果分配失败会返回负数。major传大于0的数,表示想要申请这个数当主设备号,如果成功返回0,失败返回负数;
(2)name:设备的名字;
(3)fops:设备的操作方法,都是一些函数指针;

10、地址映射

参考博客:《静态映射和动态映射》

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在起飞的蜗牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值