驱动学习-日常笔记day4

【1】复习
1.ioctl函数的使用
ioctl(fd,100,100)
---------------------
unlocked_ioctl(file,cmd,args)
{
printk(“cmd = %d,args = %d\n”,cmd,args);
//cmd=100 args=100;
}
命令码通过 ===>_IO _IOR _IOW _IORW封装
2.自动创建设备节点
class_create
device_create
class_destroy
device_destroy

【2】字符设备驱动的框架
user:
open(“设备文件的名字”,打开方式); write(fd,buf,sizeof(buf));
|
设备文件名 ---->ls -i ----->inode号(文件系统识别文件方式)
|
--------------------------------------|-----------------
kernel: |
--------------|
|
struct inode{
//文件所有的信息全在inode记录着
umode_t i_mode; //文件权限
uid_t i_uid; //用户id
gid_t i_gid; //组id
unsigned long i_ino; //inode号
dev_t i_rdev; //设备号
union {
struct block_device *i_bdev;
//块设备驱动
struct cdev *i_cdev;
//字符设备驱动
};
}

设备号1			  设备号2		       设备号n	
字符设备驱动1    字符设备驱动2       字符设备驱动n
---------         ---------            ---------
|  		|        |  	   |           |  		|
|  		|        |  	   |           |  		|
|  		|        |  	   |           |  		|
---------        ----------     ...    ----------

1.字符设备驱动的结构体
struct cdev {
	const struct file_operations *fops;
	//操作方法结构体
	struct list_head list;
	//内核链表
	dev_t dev;
	//设备号
	unsigned int count;
	//同种设备的个数
};

问:在应用程序中read write ioctl close如何通过
fd找到驱动的fops并调用里面对应的函数的?

1.open read write ioctl close函数是在哪里执行的?
	在进程中,进程的结构体对象是task_struct
	
2.既然fd是在进程中产生的,那么它就会在进程
	结构体中有记录
	struct task_struct {
		volatile long state;
		//进程的状态
		int prio, static_prio, normal_prio;
		unsigned int rt_priority;
		//进程的优先级
		pid_t pid;
		//进程号
		struct thread_struct thread;
		//进程中的线程
		struct files_struct *files;
		//打开文件时|候产生的各种信息
	}               |
				    |
	struct file  * fd_array[NR_OPEN_DEFAULT];
	
	这是fd的数组,
	fd_array[fd]===>struct file *

	struct file每打开一次文件,就问产生一个file结构体
	对象,将这个file结构体放大fd_array中,这个数组的
	下标就是这个文件的文件描述符。这个file结构体就是
	描述本次打开文件时候的各种信息的(打开方式,游标等)。
	struct file {
		const struct file_operations	*f_op;
		//操作方法结构体是从inode结构体获取的
		unsigned int 		f_flags;
		fmode_t			f_mode;
		loff_t			f_pos;
	}
	
通过fd访问到驱动中的read write close的流程如下:	
fd-->fd_array[fd]--->file-->fops-->read write close;
	

编写字符设备驱动的流程?(register_chrdev)
1.定义结构体
	struct cdev cdev;
	struct cdev *cdev = cdev_alloc(); 
	//使用cdev_alloc在内核空间分配内存

2.结构体成员的初始化
	void cdev_init(struct cdev *cdev, 
		const struct file_operations *fops)
	功能:初始化cdev的结构体,没有初始化设备号和设备的个数
	参数:
		@cdev:刚才分配好的结构体指针
		@fops:操作方法结构体
3.申请设备号
	1.静态申请(直接指定)
	int register_chrdev_region(dev_t from,
	unsigned count, const char *name)  
	功能:静态申请(直接指定)
	参数:
		@from :设备号的起始值
		@count:设备的个数
		@name :名字cat /proc/devices
	返回值:成功返回0 ,失败返回错误码
	
	2.动态申请(操作系统分配)
	int alloc_chrdev_region(dev_t *dev, 
		unsigned baseminor, unsigned count,                     
        const char *name)
	功能:.动态申请(操作系统分配)
	参数:
		@dev:返回申请到的第一个设备号
		@baseminor:次设备号的起始值 3
		@count:设备的个数
		@name :名字cat /proc/devices
	返回值:成功返回0 ,失败返回错误码
	
4.注册
	int cdev_add(struct cdev *p, dev_t dev, 
				unsigned count) 
	功能:注册字符设备驱动
	参数:
		@p:cdev结构体指针
		@dev:设备号
		@count:设备的个数
	返回值:成功返回0 ,失败返回错误码
	
-----------------------------------------------
void cdev_del(struct cdev *p)	
功能:注销字符设备驱动
参数:
	@p:cdev的结构体指针
	
	
void unregister_chrdev_region(dev_t from, unsigned count)
功能:释放设备号
参数:
	@from:设备号的起始的值
	@count:设备的个数
	
void kfree(void *p)
功能:释放cdev_alloc分配的内存
参数:
	@p:申请的内存的首地址。


练习:
	1.字符设备驱动分步实现流程(20分钟)

【3】分步实现的字符设备驱动和一个函数完成的字符设备的区别?
register_chrdev注册字符设备驱动的时候一次分配256个设备号
用户无法指定。加入在公司中要求分配的设备的个数为3或者500
个这个函数就无法完成相应的功能了。综合上述原因在看公司做
字符设备驱动开发的时候一般不使用register_chrdev这个函数
完成字符设备驱动的注册工作。

【4】设备号的问题
1.主设备号范围
0-2^12
0-4096
2.问:在使用cat /proc/devices查看的时
候设备号如何存储的

	主设备号采用哈希表的形式存储
	
	主设备号%255 ==作为哈希表数组的下标。
	
3.问:自动分配设备号的时候为什么每
	次分配的都是250	?
	自动分配设备号的时候采用的是倒叙分配,从
	254往前查找,直到找到没有被使用过的主设备
	号为止,并将这个设备号分配给你。

4.问:自动申请主设备号的范围
	自动申请设备号的范围:	1-254
	其他的主设备号如何使用,只能采用静
	态申请设备号的方式完成

【5】实现如下功能
向myled0写1 红灯亮
向myled0写0 红灯灭
向myled1写1 绿灯亮
向myled1写0 绿灯灭
向myled2写1 蓝灯亮
向myled2写0 蓝灯灭

echo 1 >/dev/myled0  红灯亮
echo 0 >/dev/myled0  红灯灭

猜你喜欢

转载自blog.csdn.net/weixin_48430195/article/details/108671800
今日推荐