6.misc类设备与蜂鸣器驱动

版权声明:转载请声明 https://blog.csdn.net/qq_40732350/article/details/83041407

 1.板载蜂鸣器驱动测试

1.1、驱动部分
(1)九鼎移植内核已经提供了蜂鸣器驱动源码
(2)make menuconfig
(3)bug排查。修改Makefile中的宏名,最终可以在系统中看到 /dev/buzzer
1.2、应用部分
(1)应用编写:打开文件+ioctl
(2)测试实践


2.misc类设备介绍

由于Linux驱动倾向于分层设计,所以各个具体的设备都可以找到它归属的类型,从而套到它相应的架构里面去,并且只需要实现最底层的那一部分。但是,也有部分类似buzzer的字符设备,确实不知道它属于什么类型,一般采用miscdevice框架结构。
2.1、何为misc

(1)中文名:杂项设备\杂散设备
(2)/sys/class/misc
(3)典型的字符设备
(4)有一套驱动框架,内核实现一部分(misc.c),驱动实现一部分(x210-buzzer.c)。
(5)misc是对原始的字符设备注册接口的一个类层次的封装,很多典型字符设备都可以归类到misc类中,使用misc驱动框架来管理。
2.2、misc类设备驱动架构
(1)内核开发者实现部分,关键点有2个:一个是类的创建,另一个是开放给驱动开发者的接口
(2)具体设备驱动工程师实现部分
2.3  misc原理

miscdevice本质上也是字符设备

只是在miscdevice核心层的misc_init()函数中,通过register_chrdev(MISC_MAJOR, "misc",&misc_fops)注册了字符设备

而具体miscdevice实例调用misc_register()的时候又自动完成了device_create()、获取动态次设备号的动作

每个misc设备的主设备号都是MISC_MAJOR = 10

misc设备结构体

struct miscdevice  {
	int minor;
	const char *name;
	const struct file_operations *fops;
	struct list_head list;
	struct device *parent;
	struct device *this_device;
	const char *nodename;
	mode_t mode;
};

如果minor为MISC_DYNAMIC_MINOR, miscdevice核心层会自动找一个空闲的次设备号,否则用minor指定的次设备号

miscdevice驱动的注册和注销分别用下面两个API:
 

int misc_register(struct miscdevice * misc);
int misc_deregister(struct miscdevice *misc);

因此miscdevice驱动的一般结构形如:
 

static const struct file_operations xxx_fops = {
.unlocked_ioctl = xxx_ioctl,
.open= xxx_open,
…
};
static struct miscdevice xxx_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "xxx",
.fops = &xxx_fops
};
static int __init xxx_init(void)
{
pr_info("ARC Hostlink driver mmap at 0x%p\n", __HOSTLINK__);
return misc_register(&xxx_dev);//注册
}
static void __exit xxx_exit(void)
{
	misc_deregister(&xxx_dev);//注销
}

2.4  struct file的好处

可以利用struct file的private_data挂接驱动的结构体,这就可以通过struct file找到驱动的结构体

每一个文件都有一个对应的结构体,这个结构体非常重要

struct file {  //有些变量我没有写上

	const struct file_operations	*f_op;  //用于挂接设备驱动的file_operations
	spinlock_t		f_lock;  /* f_ep_links, f_flags, no IRQ */
	atomic_long_t		f_count;
	unsigned int 		f_flags;
	fmode_t			f_mode;
	loff_t			f_pos;
	struct fown_struct	f_owner;
	const struct cred	*f_cred;
	struct file_ra_state	f_ra;

	u64			f_version;

	void			*private_data;  //用作私有数据,可以挂接设备结构体

	/* Used by fs/eventpoll.c to link all the hooks to this file */
	struct list_head	f_ep_links;

	unsigned long f_mnt_write_state;
};

在调用misc_register(&xxx_dev)时,该函数内部会自动调用device_create(),而device_create()会以xxx_dev作为drvdata参数。其次,在miscdevice核心层misc_open()函数的帮助下,在file_operations的成员函数中, xxx_dev会自动成为file的private_data(misc_open会完成file->private_data的赋值操作)。

static int misc_open(struct inode * inode, struct file * file)
{
	int minor = iminor(inode);//得到次设备号
	struct miscdevice *c;
	int err = -ENODEV;
	const struct file_operations *old_fops, *new_fops = NULL;

	mutex_lock(&misc_mtx);//上互斥锁
	
	list_for_each_entry(c, &misc_list, list) {
		if (c->minor == minor) {//查找次设备号相同的设备
			new_fops = fops_get(c->fops);	//得到file_operations
			break;
		}
	}
		
	if (!new_fops) {//如果为空
		mutex_unlock(&misc_mtx);
		request_module("char-major-%d-%d", MISC_MAJOR, minor);
		mutex_lock(&misc_mtx);

		list_for_each_entry(c, &misc_list, list) {
			if (c->minor == minor) {
				new_fops = fops_get(c->fops);
				break;
			}
		}
		if (!new_fops)
			goto fail;
	}

	err = 0;
	old_fops = file->f_op;//得到老的文件的file_operations
	file->f_op = new_fops;//变为自己定义的file_operations
	if (file->f_op->open) {//如果file_operations的open变量不为空
		file->private_data = c;//把设备miscdevice 挂接到文件的私有数据上
		err=file->f_op->open(inode,file);//判断能不能打开文件
		if (err) {//不能打开
			fops_put(file->f_op);
			file->f_op = fops_get(old_fops);
		}
	}
	fops_put(old_fops);
fail:
	mutex_unlock(&misc_mtx);
	return err;
}

如果我们用面向对象的封装思想把一个设备的属性、自旋锁、互斥体、等待队列、 miscdevice等封装在一个结构体里面:

struct xxx_dev {
unsigned int version;
unsigned int size;
spinlock_t lock;
//...
struct miscdevice miscdev;
};

在file_operations的成员函数中,就可以通过container_of()和file->private_data反推出xxx_dev的实例
 

static long xxx_ioctl(struct file *file, unsigned int cmd, unsigned long i)
{
struct xxx_dev *xxx = container_of(file->private_data,struct xxx_dev, miscdev);
//...
}

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

3.1、misc源码框架基础
(1)misc源码框架本身也是一个模块,内核启动时自动加载
(2)源码框架的主要工作:注册misc类,使用老接口注册字符设备驱动(主设备号10),开放device注册的接口misc_register给驱动工程师
3.2、misc类设备的注册
(1)驱动工程师需要借助misc来加载自己的驱动时,只需要调用misc_register接口注册自己的设备即可,其余均不用管。
(2)misc_list链表的作用。内核定义了一个misc_list链表用来记录所有内核中注册了的杂散类设备。当我们向内核注册一个misc类设备时,内核就会向misc_list链表中insert一个节点。
#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) }
(3)主设备号和次设备号的作用和区分


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

4.1、open函数分析
4.2、misc在proc下的展现
4.3、内核互斥锁
(1)何为互斥锁
(2)定义:DEFINE_MUTEX
(3)上锁mutex_lock和解锁mutex_unlock
(4)内核防止竞争状态的手段:原子访问、自旋锁、互斥锁、信号量
(5)原子访问主要用来做计数、自旋锁后面讲中断会详细讲、互斥锁和信号量很相似(其实就是计数值为1的信号量),互斥锁的出现比信号量晚,实现上比信号量优秀,尽量使用互斥锁。


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

5.1、dev_init
(1)信号量
(2)miscdevice
(3)gpio_request
(4)printk
5.2、ioctl
(1)为什么需要ioctl(input output control,输入输出控制)。
(2)ioctl怎么用


6.蜂鸣器驱动源码分析2
硬件操作有关的代码


 

猜你喜欢

转载自blog.csdn.net/qq_40732350/article/details/83041407
今日推荐