Linux内核.之kobj ,struct file、struct inode、struct file_operations、struct cdev之间的关系

一、struct cdev,cdev结构体与file_operations结构体

#define CHRDEV_MAJOR_HASH_SIZE 255
struct cdev {
 struct kobject kobj;
 struct module *owner;
 const struct file_operations *ops;
 struct list_head list;
 dev_t dev;
 unsigned int count;
};

在这里插入图片描述
字符设备驱动框架

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
 
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
 
#define NEWCHRLED_CNT			1		  	/* 设备号个数 */
#define NEWCHRLED_NAME			"newchrled"	/* 名字 */
#define LEDOFF 					0			/* 关灯 */
#define LEDON 					1			/* 开灯 */
 
/* 寄存器物理地址 */
#define CCM_CCGR1_BASE				(0X020C406C)	
#define SW_MUX_GPIO1_IO03_BASE		(0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE		(0X020E02F4)
#define GPIO1_DR_BASE				(0X0209C000)
#define GPIO1_GDIR_BASE				(0X0209C004)
 
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;
 
/* newchrled设备结构体 */
struct newchrled_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;		/* 类 		*/
	struct device *device;	/* 设备 	 */
	int major;				/* 主设备号	  */
	int minor;				/* 次设备号   */
};
 
struct newchrled_dev newchrled;	/* led设备 */

void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO1_DR);
		val &= ~(1 << 3);	
		writel(val, GPIO1_DR);
	}else if(sta == LEDOFF) {
		val = readl(GPIO1_DR);
		val|= (1 << 3);	
		writel(val, GPIO1_DR);
	}	
}
 
static int led_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &newchrled; /* 设置私有数据 */
	return 0;
}
 
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	return 0;
}
 
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;
 
	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}
 
	ledstat = databuf[0];		/* 获取状态值 */
 
	if(ledstat == LEDON) {	
		led_switch(LEDON);		/* 打开LED灯 */
	} else if(ledstat == LEDOFF) {
		led_switch(LEDOFF);	/* 关闭LED灯 */
	}
	return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
	return 0;
}
 
/* 设备操作函数 */
static struct file_operations newchrled_fops = {
	.owner = THIS_MODULE,
	.open = led_open,
	.read = led_read,
	.write = led_write,
	.release = 	led_release,
};
 
static int __init led_init(void)
{
	u32 val = 0;
 
	/* 初始化LED */
	/* 1、寄存器地址映射 */
  	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
	SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
  	SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
	GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
	GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);
 
	/* 2、使能GPIO1时钟 */
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);	/* 清除以前的设置 */
	val |= (3 << 26);	/* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);
 
	/* 3、设置GPIO1_IO03的复用功能,将其复用为
	 *    GPIO1_IO03,最后设置IO属性。
	 */
	writel(5, SW_MUX_GPIO1_IO03);
	
	/*寄存器SW_PAD_GPIO1_IO03设置IO属性
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 00 默认下拉
     *bit [13]: 0 kepper功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 R0/6驱动能力
     *bit [0]: 0 低转换率
	 */
	writel(0x10B0, SW_PAD_GPIO1_IO03);
 
	/* 4、设置GPIO1_IO03为输出功能 */
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 3);	/* 清除以前的设置 */
	val |= (1 << 3);	/* 设置为输出 */
	writel(val, GPIO1_GDIR);
 
	/* 5、默认关闭LED */
	val = readl(GPIO1_DR);
	val |= (1 << 3);	
	writel(val, GPIO1_DR);
 
	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	if (newchrled.major) {		/*  定义了设备号 */
		newchrled.devid = MKDEV(newchrled.major, 0);
		register_chrdev_region(newchrled.devid, NEWCHRLED_CNT, NEWCHRLED_NAME);
	} else {						/* 没有定义设备号 */
		alloc_chrdev_region(&newchrled.devid, 0, NEWCHRLED_CNT, NEWCHRLED_NAME);	/* 申请设备号 */
		newchrled.major = MAJOR(newchrled.devid);	/* 获取分配号的主设备号 */
		newchrled.minor = MINOR(newchrled.devid);	/* 获取分配号的次设备号 */
	}
	printk("newcheled major=%d,minor=%d\r\n",newchrled.major, newchrled.minor);	
	
	/* 2、初始化cdev */
	newchrled.cdev.owner = THIS_MODULE;
	cdev_init(&newchrled.cdev, &newchrled_fops);
	
	/* 3、添加一个cdev */
	cdev_add(&newchrled.cdev, newchrled.devid, NEWCHRLED_CNT);
 
	/* 4、创建类 */
	newchrled.class = class_create(THIS_MODULE, NEWCHRLED_NAME);
	if (IS_ERR(newchrled.class)) {
		return PTR_ERR(newchrled.class);
	}
 
	/* 5、创建设备 */
	newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
	if (IS_ERR(newchrled.device)) {
		return PTR_ERR(newchrled.device);
	}
	
	return 0;
}

static void __exit led_exit(void)
{
	/* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);
 
	/* 注销字符设备驱动 */
	cdev_del(&newchrled.cdev);/*  删除cdev */
	unregister_chrdev_region(newchrled.devid, NEWCHRLED_CNT); /* 注销设备号 */
 
	device_destroy(newchrled.class, newchrled.devid);
	class_destroy(newchrled.class);
}
 
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

file_operations

#include <linux/fs.h>

struct file_operations 
{
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*iterate) (struct file *, struct dir_context *);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,  loff_t len);
	int (*show_fdinfo)(struct seq_file *m, struct file *f);
};

二、struct inode , struct file

open(struct inode *inode, struct file *filp)
write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)

int mem_open(struct inode *inode, struct file *filp)
{
	struct mem_dev *dev;
 
	int num = MINOR(inode->i_rdev);	
	printk(KERN_EMERG "KERN_EMERGnum = %d\n",num);
	if(num >= MEMDEV_SUM)
		return -ENODEV;
	dev = &mem_devp[num];
 
	filp->private_data = dev;
	printk(KERN_EMERG "open ok!\n");
	return 0;
}

static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;
	struct mem_dev *dev = filp->private_data;	
	
	if(p >= MEMDEV_SIZE)
		return 0;
	if(count > MEMDEV_SIZE - p)
		count = MEMDEV_SIZE - p;
	//从用户空间向内核写数据	
	if(copy_from_user(dev->data+p,buf,count))
	{
		ret = -EFAULT;
	}
	else
	{
		*ppos += count;
		ret = count;
		printk(KERN_INFO "write %d bytes to %d\n",count,p);
	}
 
	return ret;
}
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
	unsigned long p = *ppos;
	unsigned int count = size;
	int ret = 0;
	struct mem_dev *dev = filp->private_data;
 
	if(p >= MEMDEV_SIZE)
		return 0;
	if(count > MEMDEV_SIZE - p)
		count = MEMDEV_SIZE - p;
	//读取数据到用户空间
	if(copy_to_user(buf,(void*)(dev->data+p),count))
	{
		ret = -EFAULT;
	}
	else
	{
		*ppos += count;
		ret = count;
		printk(KERN_INFO "read %d bytes from %d\n",count,p);
	}
	
	return ret;
}

struct inode 及 struct file
struct inode在<linux/fs.h>头文件里定义的struct inode结构体如下,其与磁盘上的i-node相对应。当应用层用open()访问一个文件时,会在内核中为i-node创建一个副本,主要记录如下内容。这是管理一个文件的基本要素。其中与字符驱动密切相关的是i_rdev成员,存放了设备文件对应的设备号。i_cdev则是存放着字符设备对应的cdev结构体指针,该结构体由驱动程序模块建立的。
在这里插入图片描述

在<linux/fs.h>头文件中定义的struct file结构体如下,和inode一样,其在文件被open()时,由系统创建。并获得由字符驱动模块构建的实际操作文件的函数入口file_operations结构体的指针,将指针存于f_op成员内。
在这里插入图片描述
在应用层,每个进程都会有一个文件描述符表,这个表内会存放进程打开的每一个文件的所谓文件描述符fd。实质,文件描述符表是一个数组,而fd则是这个数组元素的下标,也就是,如果fd = 0,则‘0’是指的该数组的第0个元素。fd=10,指的是该数组的第10个元素,该元素所存的内容是每个对应文件的struct file结构体指针,该结构体又存有文件的inode与file_operations结构体指针。
这就达到一个目的,当应用的任何一个操作设备文件的指令,如read(fd) , write(fd)等,都可以通过文件描述符表数组的fd下标对应的元素找到内核 的file_operations结构体指针,这样就可以调用该结构体内对应.read()和.write()的成员函数指针,从而完成实质的对字符设备的读,写操作。
在这里插入图片描述
文件描述附表
在 Linux 中,进程被描述成 task_struct 进行管理(PCB),而task_struct 中有个指针 struct files_struct* files,该指针负责描述该进程的文件相关数据信息

struct task_struct {
	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
	void *stack;
	...
	struct files_struct *files;   // 负责描述进程的文件信息数据
	...
}

而 struct files_struct* files 指向的结构体中有一个成员 struct file* fd_array,就是「文件描述符表」(新版本的内核版本可能称为 fdt)
在这里插入图片描述
这个文件描述符表,的下标(准确来说是索引)就称为「文件描述符」,而文件描述符表下标对应的对象fd_array[i]就是 struct file*,是指向内核中用来描述被打开文件的结构体
并且在修改相关配置的情况下,一个进程默认可以打开 32 或者 64 个文件,在修改的情况下,可以达到 10w 个,也就是文件描述符表的大小可以达到的大小
注意:

每个进程都会有文件描述符表,并且子进程的创建也会拷贝父进程的文件描述符表,打开相应的文件
综上所述,这里画个图:
在这里插入图片描述

可以在 struct file 中提供若干个函数指针,比如 ssize_t (*read)(),ssize_t (*write)(),然后这些函数指针直接指向驱动层提供的读写接口,指针不必关心自己到底指向了哪个函数,对于 Linux 来说,它只知道只要调用 struct file 中的 write 函数,就一定会执行对应驱动提供的写操作接口,从而以管理文件的方式,实现从底层硬件的控制
在这里插入图片描述

三、kobject
比较复杂,简单写写。:kset/kobject/ktype。

struct kobject {
         const char              *name;
         struct list_head        entry;
         struct kobject          *parent;
         struct kset             *kset;
         struct kobj_type        *ktype;
         struct kernfs_node  	 *sd;
         struct kref             kref;
         unsigned int   state_initialized:1;
         unsigned int   state_in_sysfs:1;
         unsigned int   state_add_uevent_sent:1;
         unsigned int   state_remove_uevent_sent:1;
         unsigned int uevent_suppress:1;
};

原文链接:https://blog.csdn.net/youzhangjing_/article/details/128473593
在这里插入图片描述
sysfs文件系统提供了一种用户与内核数据结构进行交互的方式,可以通过mount -t sysfs sysfs /sys来进行挂载;

Linux设备模型中,设备、驱动、总线组织成拓扑结构,通过sysfs文件系统以目录结构进行展示与管理;

Linux设备模型中,总线负责设备和驱动的匹配,设备与驱动都挂在某一个总线上,当它们进行注册时由总线负责去完成匹配,进而回调驱动的probe函数;

SoC系统中有spi, i2c, pci等实体总线用于外设的连接,而针对集成在SoC中的外设控制器,Linux内核提供一种虚拟总线platform用于这些外设控制器的连接,此外platform总线也可用于没有实体总线的外设;

在/sys目录下,bus用于存放各类总线,其中总线中会存放挂载在该总线上的驱动和设备,比如serial8250,devices存放了系统中的设备信息,class是针对不同的设备进行分类;

上边这些功能的实现,离不开kobject/kset/ktype机制的支撑,开始旅程吧。

在这里插入图片描述

注册驱动时,用到kobj

cdev_init函数原型

void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
	memset(cdev, 0, sizeof *cdev);
	INIT_LIST_HEAD(&cdev->list);
	kobject_init(&cdev->kobj, &ktype_cdev_default);
	cdev->ops = fops;
}

cdev_add函数原型

int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
	int error;
 
	p->dev = dev;
	p->count = count;
 
	error = kobj_map(cdev_map, dev, count, NULL,
			 exact_match, exact_lock, p);
	if (error)
		return error;
 
	kobject_get(p->kobj.parent);
 
	return 0;
}
#define class_create(owner, name)		\
({						\
	static struct lock_class_key __key;	\
	__class_create(owner, name, &__key);	\
})
struct class *__class_create(struct module *owner, const char *name,
			     struct lock_class_key *key)
{
	struct class *cls;
	int retval;
 
	cls = kzalloc(sizeof(*cls), GFP_KERNEL);
	if (!cls) {
		retval = -ENOMEM;
		goto error;
	}
 
	cls->name = name;
	cls->owner = owner;
	cls->class_release = class_create_release;
 
	retval = __class_register(cls, key);
	if (retval)
		goto error;
 
	return cls;
 
error:
	kfree(cls);
	return ERR_PTR(retval);
}

int __class_register(struct class *cls, struct lock_class_key *key)
{
	struct subsys_private *cp;
	int error;
 
	pr_debug("device class '%s': registering\n", cls->name);
 
	cp = kzalloc(sizeof(*cp), GFP_KERNEL);//类同bus,分配一个私有数据
	if (!cp)
		return -ENOMEM;
	klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);//初始化class 链表
	INIT_LIST_HEAD(&cp->interfaces);
	kset_init(&cp->glue_dirs);
	__mutex_init(&cp->mutex, "subsys mutex", key);
	error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
	if (error) {
		kfree(cp);
		return error;
	}
 
	/* set the default /sys/dev directory for devices of this class */
	if (!cls->dev_kobj)
		cls->dev_kobj = sysfs_dev_char_kobj;
 
#if defined(CONFIG_BLOCK)
	/* let the block class directory show up in the root of sysfs */
	if (!sysfs_deprecated || cls != &block_class)
		cp->subsys.kobj.kset = class_kset;
#else
	cp->subsys.kobj.kset = class_kset;
#endif
	cp->subsys.kobj.ktype = &class_ktype;
	cp->class = cls;
	cls->p = cp;
	
	//在/sys/class/下面创建一个目录,同时发送一个uenent时间给上层
	error = kset_register(&cp->subsys);
	if (error) {
		kfree(cp);
		return error;
	}
	error = add_class_attrs(class_get(cls));
	class_put(cls);
	return error;
}
/* 5、创建设备 */
newchrled.device = device_create(newchrled.class, NULL, newchrled.devid, NULL, NEWCHRLED_NAME);
struct device *device_create(struct class *class, struct device *parent,
			     dev_t devt, void *drvdata, const char *fmt, ...)
{
	va_list vargs;
	struct device *dev;
 
	va_start(vargs, fmt);
	dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
	va_end(vargs);
	return dev;
}
struct device *device_create_vargs(struct class *class, struct device *parent,
				   dev_t devt, void *drvdata, const char *fmt,
				   va_list args)
{
	return device_create_groups_vargs(class, parent, devt, drvdata, NULL,
					  fmt, args);
}
static struct device *
device_create_groups_vargs(struct class *class, struct device *parent,
			   dev_t devt, void *drvdata,
			   const struct attribute_group **groups,
			   const char *fmt, va_list args)
{
	struct device *dev = NULL;
	int retval = -ENODEV;
 
	if (class == NULL || IS_ERR(class))
		goto error;
 
	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		retval = -ENOMEM;
		goto error;
	}
 
	//初始化dev
	device_initialize(dev);
	dev->devt = devt;
	dev->class = class;
	dev->parent = parent;
	dev->groups = groups;
	dev->release = device_create_release;
	dev_set_drvdata(dev, drvdata);
 
	//设置kobject的名字
	retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
	if (retval)
		goto error;
 
	retval = device_add(dev); //添加设备
	if (retval)
		goto error;
 
	return dev;
 
error:
	put_device(dev);
	return ERR_PTR(retval);
}
int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;
 
	dev = get_device(dev); //增加设备的引用计数dev->kobj->kref
	if (!dev)
		goto done;
 
	if (!dev->p) {
		//私有数据没有的话,申请并初始化:是连接bus,parent,对应驱动等重要连接点
		error = device_private_init(dev);
		if (error)
			goto done;
	}
 
	/*
	 * for statically allocated devices, which should all be converted
	 * some day, we need to initialize the name. We prevent reading back
	 * the name, and force the use of dev_name()
	 */
	if (dev->init_name) {
		//用dev的init_name初始化dev->kobject->name,实际是目录名
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}
 
	/* subsystems can specify simple device enumeration */
	//dev的init_name不存在且dev->kobject->name也不存在,则用bus的dev_name和dev_id来设置目录名
	if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
		dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
 
	if (!dev_name(dev)) { //如果上面几个步骤都还没找到可设的目录名,则失败返回,设备必须要放在某个目录下
		error = -EINVAL;
		goto name_error;
	}
 
	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
 
	parent = get_device(dev->parent); //父节点引用计数加1
	kobj = get_device_parent(dev, parent); //拿到父节点
	//拿到父节点赋值给本dev->kobj.parent,确定设备父子关系,也确定了sysfs中的目录关系
	if (kobj)
		dev->kobj.parent = kobj;
 
	/* use parent numa_node */
	//设置该设备节点为-1
	if (parent)
		set_dev_node(dev, dev_to_node(parent));
 
	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
	//把内嵌的kobject注册到设备模型中,将设备加入到kobject模型中,创建sys项目目录,目录名字为kobj->name
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;
 
	/* notify platform of device entry */
	if (platform_notify)
		platform_notify(dev);
	/*创建sys目录下设备的uevent属性文件,通过它可以查看设备的uevent事件,主要是在/sys/devices/.../中添加dev的uevent属性文件*/
	error = device_create_file(dev, &dev_attr_uevent);
	if (error)
		goto attrError;
	/*实际创建的kobject都是在device下面,其他class,bus之类的里面的具体设备都是device目录下设备的符号链接,这里是在class下创建符号链接 */
	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);//创建sys目录下设备其他属性文件(添加设备属性文件)
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);//添加设备的总线属性,将设备加入到管理它的bus总线的设备连表上,创建subsystem链接文件,链接class下的具体的子系统文件夹 将设备添加到其总线的设备列表中。
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);//把设备增加到sysfs电源管理power目录(组)下,如果该设备设置电源管理相关的内容
	if (error)
		goto DPMError;
	device_pm_add(dev);//设备添加到电源管理相关的设备列表中
 
	//主设备号存在,则产生dev属性,在/dev目录下产生设备节点文件
	if (MAJOR(dev->devt)) {
		/*创建sys目录下设备的设备号属性,即major和minor /主要是在sys/devices/...中添加dev属性文件*/
		error = device_create_file(dev, &dev_attr_dev);
		if (error)
			goto DevAttrError;
		/*在/sys/dev/char/或者/sys/dev/block/创建devt的属性的连接文件,形如10:45,由主设备号和次设备号构成,指向/sys/devices/.../的具体设备目录*/
		error = device_create_sys_dev_entry(dev);//该链接文件只具备读属性,显示主设备号:次设备号,如10:45,用户空间udev响应uevent事件时,将根据设备号在/dev下创建节点文件
		if (error)
			goto SysEntryError;
 
		devtmpfs_create_node(dev);
	}
 
	/* Notify clients of device addition.  This call must come
	 * after dpm_sysfs_add() and before kobject_uevent().
	 */
	if (dev->bus)//通知客户端,有新设备加入
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_ADD_DEVICE, dev);
	/*产生一个内核uevent事件(这里是有设备加入),可以是helper,也可是通过netlink机制和用户空间通信该事件可以被内核以及应用层捕获,属于linux设备模型中热插拔机制*/
	kobject_uevent(&dev->kobj, KOBJ_ADD);
	//给设备探测寻找相对应的驱动,在bus上找dev对应的drv,主要执行__device_attach,主要进行match,sys_add,执行probe函数和绑定等操作
	bus_probe_device(dev);
	if (parent)
		klist_add_tail(&dev->p->knode_parent,//添加新设备到父设备的子列表中
			       &parent->p->klist_children);
 
	if (dev->class) {//如果改dev有所属类,则将dev的添加到类的设备列表里面
		mutex_lock(&dev->class->p->mutex);//要使用class的互斥锁
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,//dev添加到class的klist_device链表(对driver也有klist_driver链表)
			       &dev->class->p->klist_devices);
 
		/* notify any interfaces that the device is here */
		/*通知有新设备加入,执行该dev的class_intf->add_dev(),好处是只有设备匹配注册成功了,才进行其它的注册工作(如字符设备的注册,生成/dev/***节点文件)以及部分初始化工作。*/		   
		list_for_each_entry(class_intf,
				    &dev->class->p->interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->mutex);
	}
done:
	put_device(dev);
	return error;
 SysEntryError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &dev_attr_dev);
 DevAttrError:
	device_pm_remove(dev);
	dpm_sysfs_remove(dev);
 DPMError:
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	device_remove_file(dev, &dev_attr_uevent);
 attrError:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	kobject_del(&dev->kobj);
 Error:
	cleanup_device_parent(dev);
	put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值