接上:https://blog.csdn.net/wangweijundeqq/article/details/101698711
目录
三.misc驱动框架源码分析1
3.1、misc源码框架基础
misc设备初始化函数:
注册接口函数:
3.2、misc类设备的注册
四.misc驱动框架源码分析2
4.1、open函数分析
4.2、misc在proc下的展现
4.3、内核互斥锁
五.蜂鸣器驱动源码分析1
5.1、dev_init
5.2、ioctl
六.蜂鸣器驱动源码分析2
整体框架分析图:
(1)misc源码框架本身也是一个模块,内核启动时自动加载
(2)源码框架的主要工作:注册misc类,使用老接口注册字符设备驱动(主设备号10),开放device注册的接口misc_register给驱动工程师
(1)驱动工程师需要借助misc来加载自己的驱动时,只需要调用misc_register接口注册自己的设备即可,其余均不用管。驱动工程师将来写程序时, 需要定义出 miscdevice 这个结构体, 然后对其进行填充, 调用 misc_register 对设备进行注册就可以了
(2)misc_list链表的作用。内核定义了一个misc_list链表用来记录所有内核中注册了的杂散类设备。当我们向内核注册一个misc类设备时,内核就会向misc_list链表中insert一个节点。
misc_list 链表在List.h中
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
原式子:static LIST_HEAD(misc_list);
展开后:static struct list_head misc_list = { &(misc_list), &(misc_list) }
就是定义了一个链表指针, 指针初始化指向了它自己。
此链表用于内核对杂散类设备的管理。
总结:这里我们主要分析的两个函数:misc设备初始化函数和misc设备注册函数:
\kernel\drivers\char\misc.c
static int __init misc_init(void)
{
int err;
#ifdef CONFIG_PROC_FS //用于浏览misc里面的设备
proc_create("misc", 0, NULL, &misc_proc_fops);
#endif
misc_class = class_create(THIS_MODULE, "misc");//创建misc类
err = PTR_ERR(misc_class);
if (IS_ERR(misc_class))
goto fail_remove;
err = -EIO;
//下面是字符设备驱动的创建
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))//很久之前写的代码,用MISC_MAJOR指定了主设备号,没有次设备号
goto fail_printk;
misc_class->devnode = misc_devnode;
return 0;
fail_printk:
printk("unable to get major %d for misc devices\n", MISC_MAJOR);
class_destroy(misc_class);
fail_remove:
remove_proc_entry("misc", NULL);
return err;
}
int misc_register(struct miscdevice * misc)
{
struct miscdevice *c;
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list);//misc中的链表初始化,让头指针和尾指针都指向自己
mutex_lock(&misc_mtx);
list_for_each_entry(c, &misc_list, list) { //遍历注册杂散类设的链表,misc_list这个变量是搜索不到的,在当前文件的最开头
if (c->minor == misc->minor) { //判断我们指定的minor次设备号是否已经存在,如果已经存在则出错
mutex_unlock(&misc_mtx);
return -EBUSY;
}
}
if (misc->minor == MISC_DYNAMIC_MINOR) { //把次设备号设置为255,内核会帮我们自动分配次设备号
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);//使用位图,一个bit表示一个次设备号
if (i >= DYNAMIC_MINORS) { //如果找到的可以分配次设备号已经超限
mutex_unlock(&misc_mtx);
return -EBUSY;
}
misc->minor = DYNAMIC_MINORS - i - 1; //分配次设备号
set_bit(i, misc_minors); //并且将分配的这1位置1,标志这一位已经使用了
}
dev = MKDEV(MISC_MAJOR, misc->minor); //用主次设备号给他合成一个device的设备号
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name); //给 misc类中创建设设备
if (IS_ERR(misc->this_device)) {
int i = DYNAMIC_MINORS - misc->minor - 1;
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors);
err = PTR_ERR(misc->this_device); //创建的设备添加到misc链表里面去
goto out;
}
/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
list_add(&misc->list, &misc_list);//创建的设备添加到misc链表里面来
out:
mutex_unlock(&misc_mtx);
return err;
}
主设备号用来指定一类设备,相当于个类;次设备号用于指定一类设备中的某一个设备
在 misc_init 初始化函数中调用 register_chrdev 创建 misc 设备驱动的时候, 用到了
misc_fops 结构, 此结构中包含 open 函数:
此结构中只实现了.open 函数, 我们写驱动程序的源代码的时候就需要编写这样的 open 函数等( 如果原厂没有给我们实现) 。
用户是通过 misc_open 函数, 最后在这里函数里面映射到了驱动中真正提供的 open 函
数, 最后调用了驱动中提供的 open 函数, 大概思路如下:
kernel\drivers\char\misc.c
//inode 表示设备节点、 路径; file 表示对应的文件
static int misc_open(struct inode * inode, struct file * file)
{
......
//(1)遍历杂散类的设备链表, 找次设备号
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
new_fops = fops_get(c->fops); //读出来我们查到的 fops
break;
}
}
//(2)如果上面没找到次设备号, request_module 后再找一遍
if (!new_fops) {
.....
list_for_each_entry(c, &misc_list, list) {
....
}
.....
//(3)如果找到了, 则调用 open 驱动中的函数
err = 0;
old_fops = file->f_op;
file->f_op = new_fops;
if (file->f_op->open) {
file->private_data = c;
//这才是真正的 open,真正写驱动的人实现的。
//注意: f_op 表示 file_operations -> 中的 open 等函数,
//它就是写驱动的用户实现的
err=file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
}
.....
}
在我们蜂鸣器驱动程序:
kernel\drivers\char\buzzer\x210-buzzer.c文件中
static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = x210_pwm_open, .release = x210_pwm_close, .ioctl = x210_pwm_ioctl, }; |
这个结构中的实例是有驱动工程师定义并填充的。
这样就能够对上号: misc 框架中的 misc_open 函数最后调用了驱动中的 open 函数。
注意上面的这种对齐方式:
上图这是系统里面已经注册的杂散类设备, 那么这是怎么出现的呢? 就是通过链表来遍历的。
seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); //minor 左对齐, name 右对齐打印
(1)何为互斥锁
(2)定义:DEFINE_MUTEX
(3)上锁mutex_lock和解锁mutex_unlock
(4)内核防止竞争状态的手段:原子访问、自旋锁、互斥锁、信号量
(5)原子访问主要用来做计数、自旋锁后面讲中断会详细讲、互斥锁和信号量很相似(其实就是计数值为1的信号量),互斥锁的出现比信号量晚,实现上比信号量优秀,尽量使用互斥锁。
(1)信号量
(2)miscdevice
(3)gpio_request
(4)printk
完成了上面的初始化, 表示引脚可以工作了, 但需要应用程序来具体的控制。
(1)为什么需要ioctl(input output control,输入输出控制)。
(2)ioctl怎么用
我们只需要在应用层中使用ioctl函数即可,应用层和驱动层会自动关联,具体操作是怎么实现的,无须关心,只需要知道填充了dev_fops结构体,和驱动层的x210_pwm_ioctl函数关联了即可。
下面需要关心的就是在x210_pwm_ioctl函数里面和硬件操作有关的两个函数了
PWM_Set_Freq函数
PWM_Stop函数
硬件操作有关的代码