platform总线--从设备找到驱动的过程


  platform总线驱动的注册过程,和input设备 驱动注册过程很像,都是逐个遍历设备,检查是否和驱动匹配。

  由此联想,platform总线加载设备的过程,应该也是遍历驱动,看是否和设备匹配。


2.1 注册设备和总线类型

注册设备使用的是 platform_device_add 函数---/driver/base/Platform.c

int platform_device_add(struct platform_device *pdev)
{
	int i, ret = 0;

	if (!pdev)
		return -EINVAL;

	if (!pdev->dev.parent)
		pdev->dev.parent = &platform_bus;

	pdev->dev.bus = &platform_bus_type;

	if (pdev->id != -1)
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
	else
		dev_set_name(&pdev->dev, "%s", pdev->name);

platform_device_add 函数第一部分是设置设备的 父设备 和 总线类型





6.2.2 注册设备的资源

  platform_device_add函数第二部分注册设备的资源:

 -------把设备IO端口和IO内存资源注册到系统

	for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];

		if (r->name == NULL)
			r->name = dev_name(&pdev->dev);

		p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM)
				p = &iomem_resource;
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;
		}

		if (p && insert_resource(p, r)) {
			printk(KERN_ERR
			       "%s: failed to claim resource %d\n",
			       dev_name(&pdev->dev), i);
			ret = -EBUSY;
			goto failed;
		}
	}
}

  注意 I/O端口设备控制寄存器) 和 I/O内存 注册 到系统 的代码.
  --设备基本概念一章,PCI总线通过扫描设备的配置文件,可以配置设备的I/O端口 和 I/O内存 ,platform总线在物理上并不存在,不能自动扫描,如何配置如下:
  --从内核驱动中,platform设备 很多使用探测的方式:--一般先往一个I/O端口写数据,看是否回应判断设备的 I/O端口是否存在。至于本章的q40kbd设备,他甚至没有注册自己的I/O端口和内存,而是直接使用缺省值,这是一个很古老的设备。

	pr_debug("Registering platform device '%s'. Parent at %s\n",
		 dev_name(&pdev->dev), dev_name(pdev->dev.parent));

	ret = device_add(&pdev->dev);
	if (ret == 0)
		return ret;

 failed:
	while (--i >= 0) {
		struct resource *r = &pdev->resource[i];
		unsigned long type = resource_type(r);

		if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
			release_resource(r);
	}

	return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);

啊啊

  pdev->resource[i]各个部分的分析:

/*
 * Resources are tree-like, allowing
 * nesting etc..
 */
struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	struct resource *parent, *sibling, *child;
};

  platform_device 结构体介绍:

struct platform_device {
	const char	* name;
	int		id;
	struct device	dev;
	u32		num_resources;
	struct resource	* resource;

	const struct platform_device_id	*id_entry;

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};




2.3 增加一个设备对象

  platform_device_add 函数第三部分调用 device_add 增加一个设备对象 ,代码:

/**
 *	device_add - add device to device hierarchy.
 *	@dev:	device.
 *
 *	This is part 2 of device_register(), though may be called
 *	separately _iff_ device_initialize() has been called separately.
 *
 *	This adds it to the kobject hierarchy via kobject_add(), adds it
 *	to the global and sibling lists for the device, then
 *	adds it to the other relevant subsystems of the driver model.
 */
int device_add(struct device *dev)
{
	struct device *parent = NULL;
	char *class_name = NULL;
	int error = -EINVAL;

	dev = get_device(dev);
	if (!dev || !strlen(dev->bus_id))
		goto Error;

	parent = get_device(dev->parent);
	//--获取父设备-----------------------------------------
	pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);

	/* first, register with generic layer. */
	kobject_set_name(&dev->kobj, "%s", dev->bus_id);
	if (parent)
		dev->kobj.parent = &parent->kobj;
	//--在sys目录生成设备目录-----------------------
	if ((error = kobject_add(&dev->kobj)))
		goto Error;
	//--设置uevent属性--------------------------------
	dev->uevent_attr.attr.name = "uevent";
	dev->uevent_attr.attr.mode = S_IWUSR;
	if (dev->driver)
		dev->uevent_attr.attr.owner = dev->driver->owner;
	dev->uevent_attr.store = store_uevent;
	device_create_file(dev, &dev->uevent_attr);

    函数device_add第一部分要为设备在sys目录创建目录和uevent属性文件。这些内容出现多次,不分析

	//设备的属性文件
	if (MAJOR(dev->devt)) {
		struct device_attribute *attr;
		attr = kzalloc(sizeof(*attr), GFP_KERNEL);
		if (!attr) {
			error = -ENOMEM;
			goto PMError;
		}
		attr->attr.name = "dev";
		attr->attr.mode = S_IRUGO;
		if (dev->driver)
			attr->attr.owner = dev->driver->owner;
		attr->show = show_dev;
		error = device_create_file(dev, attr);
		if (error) {
			kfree(attr);
			goto attrError;
		}

		dev->devt_attr = attr;
	}
	//--创建设备的符号链接
	if (dev->class) {
		sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj,
				  "subsystem");
		sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
				  dev->bus_id);

		sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
		class_name = make_class_name(dev->class->name, &dev->kobj);
		sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
	}

	//--设备的能源管理
	if ((error = device_pm_add(dev)))
		goto PMError;
	if ((error = bus_add_device(dev)))
		goto BusError;
	kobject_uevent(&dev->kobj, KOBJ_ADD);

    第二部分为设备创建一堆符号链接和设备属性文件

	bus_attach_device(dev);
	if (parent)
		klist_add_tail(&dev->knode_parent, &parent->klist_children);

    第三部分调用bus_attach_device 把设备注册到总线,代码如下:

/**
 *	bus_attach_device - add device to bus
 *	@dev:	device tried to attach to a driver
 *
 *	- Try to attach to driver.
 */
void bus_attach_device(struct device * dev)
{
	struct bus_type * bus = dev->bus;

	if (bus) {
		device_attach(dev);
		klist_add_tail(&dev->knode_bus, &bus->klist_devices);
	}
}

/**
 *	device_attach - try to attach device to a driver.
 *	@dev:	device.
 *
 *	Walk the list of drivers that the bus has and call
 *	driver_probe_device() for each pair. If a compatible
 *	pair is found, break out and return.
 *
 *	Returns 1 if the device was bound to a driver;
 *	0 if no matching device was found; error code otherwise.
 *
 *	When called for a USB interface, @dev->parent->sem must be held.
 */
int device_attach(struct device * dev)
{
	int ret = 0;

	down(&dev->sem);
	if (dev->driver) {
		//--设备已经有了驱动,执行sysfs的连接和链表操作---------------------
		device_bind_driver(dev);
		ret = 1;
	} else
		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
	up(&dev->sem);
	return ret;
}

  上一节添加驱动到总线的处理函数函数是bus_for_each_dev,添加设备到总线的处理函数bus_for_each_drv;
  --bus_for_each_dev:遍历设置,为驱动寻找合适设备
  --bus_for_each_drv:遍历驱动,为设备寻找合适的驱动


  platform总线驱动的注册过程,和input设备 驱动注册过程很像,都是逐个遍历设备,检查是否和驱动匹配。

  由此联想,platform总线加载设备的过程,应该也是遍历驱动,看是否和设备匹配。


2.1 注册设备和总线类型

注册设备使用的是 platform_device_add 函数---/driver/base/Platform.c

int platform_device_add(struct platform_device *pdev)
{
	int i, ret = 0;

	if (!pdev)
		return -EINVAL;

	if (!pdev->dev.parent)
		pdev->dev.parent = &platform_bus;

	pdev->dev.bus = &platform_bus_type;

	if (pdev->id != -1)
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
	else
		dev_set_name(&pdev->dev, "%s", pdev->name);

platform_device_add 函数第一部分是设置设备的 父设备 和 总线类型





6.2.2 注册设备的资源

  platform_device_add函数第二部分注册设备的资源:

 -------把设备IO端口和IO内存资源注册到系统

	for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];

		if (r->name == NULL)
			r->name = dev_name(&pdev->dev);

		p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM)
				p = &iomem_resource;
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;
		}

		if (p && insert_resource(p, r)) {
			printk(KERN_ERR
			       "%s: failed to claim resource %d\n",
			       dev_name(&pdev->dev), i);
			ret = -EBUSY;
			goto failed;
		}
	}
}

  注意 I/O端口设备控制寄存器) 和 I/O内存 注册 到系统 的代码.
  --设备基本概念一章,PCI总线通过扫描设备的配置文件,可以配置设备的I/O端口 和 I/O内存 ,platform总线在物理上并不存在,不能自动扫描,如何配置如下:
  --从内核驱动中,platform设备 很多使用探测的方式:--一般先往一个I/O端口写数据,看是否回应判断设备的 I/O端口是否存在。至于本章的q40kbd设备,他甚至没有注册自己的I/O端口和内存,而是直接使用缺省值,这是一个很古老的设备。

	pr_debug("Registering platform device '%s'. Parent at %s\n",
		 dev_name(&pdev->dev), dev_name(pdev->dev.parent));

	ret = device_add(&pdev->dev);
	if (ret == 0)
		return ret;

 failed:
	while (--i >= 0) {
		struct resource *r = &pdev->resource[i];
		unsigned long type = resource_type(r);

		if (type == IORESOURCE_MEM || type == IORESOURCE_IO)
			release_resource(r);
	}

	return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);

啊啊

  pdev->resource[i]各个部分的分析:

/*
 * Resources are tree-like, allowing
 * nesting etc..
 */
struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	struct resource *parent, *sibling, *child;
};

  platform_device 结构体介绍:

struct platform_device {
	const char	* name;
	int		id;
	struct device	dev;
	u32		num_resources;
	struct resource	* resource;

	const struct platform_device_id	*id_entry;

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};




2.3 增加一个设备对象

  platform_device_add 函数第三部分调用 device_add 增加一个设备对象 ,代码:

/**
 *	device_add - add device to device hierarchy.
 *	@dev:	device.
 *
 *	This is part 2 of device_register(), though may be called
 *	separately _iff_ device_initialize() has been called separately.
 *
 *	This adds it to the kobject hierarchy via kobject_add(), adds it
 *	to the global and sibling lists for the device, then
 *	adds it to the other relevant subsystems of the driver model.
 */
int device_add(struct device *dev)
{
	struct device *parent = NULL;
	char *class_name = NULL;
	int error = -EINVAL;

	dev = get_device(dev);
	if (!dev || !strlen(dev->bus_id))
		goto Error;

	parent = get_device(dev->parent);
	//--获取父设备-----------------------------------------
	pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);

	/* first, register with generic layer. */
	kobject_set_name(&dev->kobj, "%s", dev->bus_id);
	if (parent)
		dev->kobj.parent = &parent->kobj;
	//--在sys目录生成设备目录-----------------------
	if ((error = kobject_add(&dev->kobj)))
		goto Error;
	//--设置uevent属性--------------------------------
	dev->uevent_attr.attr.name = "uevent";
	dev->uevent_attr.attr.mode = S_IWUSR;
	if (dev->driver)
		dev->uevent_attr.attr.owner = dev->driver->owner;
	dev->uevent_attr.store = store_uevent;
	device_create_file(dev, &dev->uevent_attr);

    函数device_add第一部分要为设备在sys目录创建目录和uevent属性文件。这些内容出现多次,不分析

	//设备的属性文件
	if (MAJOR(dev->devt)) {
		struct device_attribute *attr;
		attr = kzalloc(sizeof(*attr), GFP_KERNEL);
		if (!attr) {
			error = -ENOMEM;
			goto PMError;
		}
		attr->attr.name = "dev";
		attr->attr.mode = S_IRUGO;
		if (dev->driver)
			attr->attr.owner = dev->driver->owner;
		attr->show = show_dev;
		error = device_create_file(dev, attr);
		if (error) {
			kfree(attr);
			goto attrError;
		}

		dev->devt_attr = attr;
	}
	//--创建设备的符号链接
	if (dev->class) {
		sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj,
				  "subsystem");
		sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
				  dev->bus_id);

		sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
		class_name = make_class_name(dev->class->name, &dev->kobj);
		sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
	}

	//--设备的能源管理
	if ((error = device_pm_add(dev)))
		goto PMError;
	if ((error = bus_add_device(dev)))
		goto BusError;
	kobject_uevent(&dev->kobj, KOBJ_ADD);

    第二部分为设备创建一堆符号链接和设备属性文件

	bus_attach_device(dev);
	if (parent)
		klist_add_tail(&dev->knode_parent, &parent->klist_children);

    第三部分调用bus_attach_device 把设备注册到总线,代码如下:

/**
 *	bus_attach_device - add device to bus
 *	@dev:	device tried to attach to a driver
 *
 *	- Try to attach to driver.
 */
void bus_attach_device(struct device * dev)
{
	struct bus_type * bus = dev->bus;

	if (bus) {
		device_attach(dev);
		klist_add_tail(&dev->knode_bus, &bus->klist_devices);
	}
}

/**
 *	device_attach - try to attach device to a driver.
 *	@dev:	device.
 *
 *	Walk the list of drivers that the bus has and call
 *	driver_probe_device() for each pair. If a compatible
 *	pair is found, break out and return.
 *
 *	Returns 1 if the device was bound to a driver;
 *	0 if no matching device was found; error code otherwise.
 *
 *	When called for a USB interface, @dev->parent->sem must be held.
 */
int device_attach(struct device * dev)
{
	int ret = 0;

	down(&dev->sem);
	if (dev->driver) {
		//--设备已经有了驱动,执行sysfs的连接和链表操作---------------------
		device_bind_driver(dev);
		ret = 1;
	} else
		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
	up(&dev->sem);
	return ret;
}

  上一节添加驱动到总线的处理函数函数是bus_for_each_dev,添加设备到总线的处理函数bus_for_each_drv;
  --bus_for_each_dev:遍历设置,为驱动寻找合适设备
  --bus_for_each_drv:遍历驱动,为设备寻找合适的驱动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值