【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 红灯灭