七.misc类设备与蜂鸣器驱动(下)

接上: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


三.misc驱动框架源码分析1

  1. 3.1、misc源码框架基础

整体框架分析图:

misc设备初始化函数:

七.misc类设备与蜂鸣器驱动(下)_第1张图片

注册接口函数:

七.misc类设备与蜂鸣器驱动(下)_第2张图片

(1)misc源码框架本身也是一个模块,内核启动时自动加载

(2)源码框架的主要工作:注册misc类,使用老接口注册字符设备驱动(主设备号10),开放device注册的接口misc_register给驱动工程师

七.misc类设备与蜂鸣器驱动(下)_第3张图片

  1. 3.2、misc类设备的注册

(1)驱动工程师需要借助misc来加载自己的驱动时,只需要调用misc_register接口注册自己的设备即可,其余均不用管。驱动工程师将来写程序时, 需要定义出 miscdevice 这个结构体, 然后对其进行填充, 调用 misc_register 对设备进行注册就可以了

七.misc类设备与蜂鸣器驱动(下)_第4张图片

(2)misc_list链表的作用。内核定义了一个misc_list链表用来记录所有内核中注册了的杂散类设备。当我们向内核注册一个misc类设备时,内核就会向misc_list链表中insert一个节点。

七.misc类设备与蜂鸣器驱动(下)_第5张图片

misc_list 链表在List.h中

七.misc类设备与蜂鸣器驱动(下)_第6张图片

#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;
}

  1. (3)主设备号和次设备号的作用和区分

主设备号用来指定一类设备,相当于个类;次设备号用于指定一类设备中的某一个设备

四.misc驱动框架源码分析2

4.1、open函数分析

在 misc_init 初始化函数中调用 register_chrdev 创建 misc 设备驱动的时候, 用到了

misc_fops 结构, 此结构中包含 open 函数:

七.misc类设备与蜂鸣器驱动(下)_第7张图片

此结构中只实现了.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 函数。

4.2、misc在proc下的展现

七.misc类设备与蜂鸣器驱动(下)_第8张图片

注意上面的这种对齐方式:

上图这是系统里面已经注册的杂散类设备, 那么这是怎么出现的呢? 就是通过链表来遍历的。

七.misc类设备与蜂鸣器驱动(下)_第9张图片

seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : ""); //minor 左对齐, name 右对齐打印

4.3、内核互斥锁

(1)何为互斥锁

(2)定义:DEFINE_MUTEX

(3)上锁mutex_lock和解锁mutex_unlock

(4)内核防止竞争状态的手段:原子访问、自旋锁、互斥锁、信号量

(5)原子访问主要用来做计数、自旋锁后面讲中断会详细讲、互斥锁和信号量很相似(其实就是计数值为1的信号量),互斥锁的出现比信号量晚,实现上比信号量优秀,尽量使用互斥锁。

五.蜂鸣器驱动源码分析1

5.1、dev_init

(1)信号量

(2)miscdevice

(3)gpio_request

(4)printk

七.misc类设备与蜂鸣器驱动(下)_第10张图片

完成了上面的初始化, 表示引脚可以工作了, 但需要应用程序来具体的控制。

5.2、ioctl

(1)为什么需要ioctl(input output control,输入输出控制)。

七.misc类设备与蜂鸣器驱动(下)_第11张图片

(2)ioctl怎么用

我们只需要在应用层中使用ioctl函数即可,应用层和驱动层会自动关联,具体操作是怎么实现的,无须关心,只需要知道填充了dev_fops结构体,和驱动层的x210_pwm_ioctl函数关联了即可。

下面需要关心的就是在x210_pwm_ioctl函数里面和硬件操作有关的两个函数了

PWM_Set_Freq函数

PWM_Stop函数

六.蜂鸣器驱动源码分析2

硬件操作有关的代码

七.misc类设备与蜂鸣器驱动(下)_第12张图片

七.misc类设备与蜂鸣器驱动(下)_第13张图片

你可能感兴趣的:(ARM+Linux探索之旅,ARM(linux驱动开发))