Linux Platform总线学习

Platform 平台驱动学习

1 Platform总线、设备、驱动介绍

1.1 什么是Platform总线

Platform是一种虚拟、抽象的总线,其将设备与驱动绑定在一起,系统每在注册设备时会寻找与之匹配的的驱动,同理系统每在注册驱动时也会去寻找与之匹配的设备,而该部分工作就是由总线来完成的。
在这里插入图片描述
由图片可知,Platform设备在内核中的实现主要包括三个部分:

  1. Platform Bus,基于底层bus模块,抽象出一个虚拟的Platform bus,用于挂载Platform设备;
  2. Platform Device,基于底层device模块,抽象出Platform Device,用于表示Platform设备;
  3. Platform Driver,基于底层device_driver模块,抽象出Platform Driver,用于驱动Platform设备

1.2 Platform设备

platform_device结构体(include/linux/platform_device.h)如下所述:
struct platform_device {
const char * name; //platform设备名字
int id; //用于区分设备名相同时将在设备名后面追加该ID
struct device dev; //内置device结构体
u32 num_resources; //资源结构体数量
struct resource * resource; //指向资源结构体数组

	const struct platform_device_id *id_entry;//用来进行与设备驱动匹配用的id_table表

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

	/* arch specific additions */
	struct pdev_archdata	archdata;	//私有数据,添加自己的东西
};

struct resource结构体分析:

struct resource {
			resource_size_t start;	//资源起始值
			resource_size_t end;	//资源结束值
			const char *name;		资源名
			unsigned long flags;	//资源的标识,指的是资源的类型
			struct resource *parent, *sibling, *child;	//资源指针,构成资源链表
		};

1.3 Platform驱动

platform_driver结构体(include/linux/platform_device.h)如下所述:

struct platform_driver {
		int (*probe)(struct platform_device *);	//平台驱动初始化时会调用该函数
		int (*remove)(struct platform_device *);	//平台驱动卸载时会调用该函数
		void (*shutdown)(struct platform_device *);	//平台驱动关闭时会调用该函数
		int (*suspend)(struct platform_device *, pm_message_t state);
		int (*resume)(struct platform_device *);	//平台驱动释放时会调用该函数
		struct device_driver driver;				//内置device_driver结构体
		const struct platform_device_id *id_table;	//该设备驱动支持的设备列表,通过该指针指向platform_device_id类型的数组
	};

2 Platform总线的初始化

Platform总线的注册
Bus_type结构体(include/linux/device.h):

/**
 * struct bus_type - The bus type of the device
 *
 * @name:	The name of the bus.
 * @bus_attrs:	Default attributes of the bus.
 * @dev_attrs:	Default attributes of the devices on the bus.
 * @drv_attrs:	Default attributes of the device drivers on the bus.
 * @match:	Called, perhaps multiple times, whenever a new device or driver
 *		is added for this bus. It should return a nonzero value if the
 *		given device can be handled by the given driver.
 * @uevent:	Called when a device is added, removed, or a few other things
 *		that generate uevents to add the environment variables.
 * @probe:	Called when a new device or driver add to this bus, and callback
 *		the specific driver's probe to initial the matched device.
 * @remove:	Called when a device removed from this bus.
 * @shutdown:	Called at shut-down time to quiesce the device.
 * @suspend:	Called when a device on this bus wants to go to sleep mode.
 * @resume:	Called to bring a device on this bus out of sleep mode.
 * @pm:		Power management operations of this bus, callback the specific
 *		device driver's pm-ops.
 * @p:		The private data of the driver core, only the driver core can
 *		touch this.
 *
 * A bus is a channel between the processor and one or more devices. For the
 * purposes of the device model, all devices are connected via a bus, even if
 * it is an internal, virtual, "platform" bus. Buses can plug into each other.
 * A USB controller is usually a PCI device, for example. The device model
 * represents the actual connections between buses and the devices they control.
 * A bus is represented by the bus_type structure. It contains the name, the
 * default attributes, the bus' methods, PM operations, and the driver core's
 * private data.
 */
struct bus_type {
	const char		*name;			//总线名字
	struct bus_attribute	*bus_attrs;		//总线属性
	struct device_attribute	*dev_attrs;	//总线下的设备的属性
	struct driver_attribute	*drv_attrs;	//总线下的驱动属性

	int (*match)(struct device *dev, struct device_driver *drv);	//总线下的设备与驱动的匹配函数
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);//事件函数
	int (*probe)(struct device *dev);		//探针函数,一般用于驱动初始化
	int (*remove)(struct device *dev);
	void (*shutdown)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct dev_pm_ops *pm;		//电源管理相关的

	struct subsys_private *p;				//总线私有数据
};

Subsys_private结构体(driver/base/base.h):

  /**
     * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
     *
     * @subsys - the struct kset that defines this subsystem
     * @devices_kset - the list of devices associated
     *
     * @drivers_kset - the list of drivers associated
     * @klist_devices - the klist to iterate over the @devices_kset
     * @klist_drivers - the klist to iterate over the @drivers_kset
     * @bus_notifier - the bus notifier list for anything that cares about things
     *                 on this bus.
     * @bus - pointer back to the struct bus_type that this structure is associated
     *        with.
    * @class_interfaces - list of class_interfaces associated
     * @glue_dirs - "glue" directory to put in-between the parent device to
     *              avoid namespace conflicts
     * @class_mutex - mutex to protect the children, devices, and interfaces lists.
     * @class - pointer back to the struct class that this structure is associated
     *          with.
     *
     * This structure is the one that is the actual kobject allowing struct
     * bus_type/class to be statically allocated safely.  Nothing outside of the
     * driver core should ever touch these fields.
     */
    struct subsys_private {
    	struct kset subsys;			//bus的kset
    	struct kset *devices_kset;	//指向该总线的devices目录的
    
    	struct kset *drivers_kset;		//指向该总线的drivers目录的
    	struct klist klist_devices;		//用于挂接该总线下设备的链表头
    	struct klist klist_drivers;		//用于挂接该总线下驱动的链表头
    	struct blocking_notifier_head bus_notifier;
    	unsigned int drivers_autoprobe:1;	//是否需要在设备驱动注册的时候自动匹配设备
    	struct bus_type *bus;		//指向本bus结构体
    
    	struct list_head class_interfaces;
    	struct kset glue_dirs;
    	struct mutex class_mutex;
    	struct class *class;
    };

Driver/base/platform:platform_bus_init( )

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs		= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm			= &platform_dev_pm_ops,
};

平台总线初始化:

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();

	error = device_register(&platform_bus);	//注册设备
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);	//注册总线
	if (error)
		device_unregister(&platform_bus);
	return error;
}

Drivers/base/bus.c:bus_register ( )

/**
 * bus_register - register a bus with the system.
 * @bus: bus.
 *
 * Once we have that, we registered the bus with the kobject
 * infrastructure, then register the children subsystems it has:
 * the devices and drivers that belong to the bus.
 */
int bus_register(struct bus_type *bus)
{
	int retval;
	struct subsys_private *priv;		//定义bus私有数据结构体指针

	priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->bus = bus;		//指向需总线结构体
	bus->p = priv;			//将bus与私有数据建立关系

	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);//设置总线的名字
	if (retval)
		goto out;

	priv->subsys.kobj.kset = bus_kset;	//设置bus队形的父对象,即sys/bus为该bus的上层目录
	priv->subsys.kobj.ktype = &bus_ktype;	//设置bus对象类型为bus_ktype
	priv->drivers_autoprobe = 1;		//配置注册设备或注册设备驱动时自动匹配

// 注册kset结构体(内部会调用kobject_add_internal函数,也就是将bus对象添加// 到/sys/bus/目录下, /sys/bus/xxx_busType 对应具体的总线
	retval = kset_register(&priv->subsys); 
	if (retval)
		goto out;
	
	//在该bus下建立属性文件 (对应的就是 bus下的 uevent属性)
	retval = bus_create_file(bus, &bus_attr_uevent);
	if (retval)
		goto bus_uevent_fail;

// 在 具 体 总 线 的 目 录 下 创 建 kset 容 器 对 象/sys/bus/xxx_busType/devices
// 通过priv->devices_kset指针去指向 这个目录对应的对象
	priv->devices_kset = kset_create_and_add("devices", NULL,
						 &priv->subsys.kobj);
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}

// 在 具 体 总 线 的 目 录 下 创 建 kset 容 器 对 象/sys/bus/xxx_busType/ drivers
// 通过 priv->drivers_kset 指针去指向 这个目录对应的对象
	priv->drivers_kset = kset_create_and_add("drivers", NULL,
						 &priv->subsys.kobj);
	if (!priv->drivers_kset) {
		retval = -ENOMEM;
		goto bus_drivers_fail;
	}

	// 初始化链表 klist_devices
	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
	// 初始化链表 klist_drivers
	klist_init(&priv->klist_drivers, NULL, NULL);

// 添加探针文件 其实内部做的还是添加属性文件 /sys/bus/xxx_busType/drivers_probe/sys/bus/xxx_busType/drivers_autoprobe
	retval = add_probe_files(bus);
	if (retval)
		goto bus_probe_files_fail;

// 根 据 bus->bus_attrs 中的属性设置来添加属性文件
	retval = bus_add_attrs(bus);
	if (retval)
		goto bus_attrs_fail;

	pr_debug("bus: '%s': registered\n", bus->name);
	return 0;

bus_attrs_fail:
	remove_probe_files(bus);
bus_probe_files_fail:
	kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
	kset_unregister(bus->p->devices_kset);
bus_devices_fail:
	bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
	kset_unregister(&bus->p->subsys);
out:
	kfree(bus->p);
	bus->p = NULL;
	return retval;
}
EXPORT_SYMBOL_GPL(bus_register);

3 Platform设备与驱动的注册

3.1 Platform设备的注册与注销

3.1.1 板文件注册方法

为了完成在板文件添加platform设备,则需在相应的板文件中申请相关的资源,并调用函数Platform_device_add()完成设备的注册,具体如下:
A. 分配platform设备资源
Arch/…/…/device.c(一般每个板都应该有一个其设备资源的管理文件)

static struct resource resources_uart1[] = {    //FAXUART
        {
                .start  = S5PV210_UART_IRQ,
                .end   = S5PV210_UART_IRQ,
                .flags  = IORESOURCE_IRQ,
        },
        {
                .start  = S5PV210_UART_PHYS,
                .end    = S5PV210_UART_PHYS + S5PV210_UART_SIZE - 1,
                .flags  = IORESOURCE_MEM,
        },
};

struct platform_device s5pv210_device_uart1 = {
	.name   = "s5pv210_uart",
	.id = 0,
	.num_resources  = ARRAY_SIZE(resources_uart1),
	.resource   = resources_uart1,
};

Arch/../../mach-s5pv210.c
static struct platform_device *devices[] __initdata = {
        & s5pv210_device_uart1  // s5pv210_device_uart1
		…;
		…;
};

B. 板文件初始化
Arch/…/…/mach-s5pv210.c:board_devices_init ()

static int __init board_devices_init(void)
{
	…;
	s5pv210_i2c_devices_init();	//I2C初始化
	spi_board_init();			//SPI初始化
	platform_add_devices(devices, ARRAY_SIZE(devices));
	…;
}

Drivers/base/platform.c: platform_add_devices()

/**
 * platform_add_devices - add a numbers of platform devices
 * @devs: array of platform devices to add
 * @num: number of platform devices in array
 */
int platform_add_devices(struct platform_device **devs, int num)
{
	int i, ret = 0;

	for (i = 0; i < num; i++) {
		ret = platform_device_register(devs[i]);
		if (ret) {
			while (--i >= 0)
				platform_device_unregister(devs[i]);
			break;
		}
	}

	return ret;
}

3.1.2 单一设备的注册方法

Drivers/base/platform.c: platform_device_register ()
通过分析,在板文件中注册platform设备,其最终调用还是调用platform_device_register()完成相关的注册。
在platform_device_register中将会调用platform_add_device()完成设备的注册

/**
 * platform_device_add - add a platform device to device hierarchy
 * @pdev: platform device we're adding
 *
 * This is part 2 of platform_device_register(), though may be called
 * separately _iff_ pdev was allocated by platform_device_alloc().
 */
int platform_device_add(struct platform_device *pdev)
{
	int i, ret = 0;

	if (!pdev)
		return -EINVAL;

	if (!pdev->dev.parent)
		// 将平台设 备 的 父 设 备 设 置 为 platform_bus ( 对 应 的 就 是/sys/devices/platform 这个目录)
pdev->dev.parent = &platform_bus; 

	pdev->dev.bus = &platform_bus_type;		//将设备挂接在platform总线下

	//设置平台设备的名字
	if (pdev->id != -1)
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
	else
		dev_set_name(&pdev->dev, "%s", pdev->name);
	
	//对平台资源的处理
	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;
		}
	}

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

	// 将平台设备添加到系统中去 /sys/devices/platform/xxx
	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);

3.1.3 Platform设备与设备驱动的匹配过程

Drivers/base/core.c: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 @dev 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.
 *
 * NOTE: _Never_ directly free @dev after calling this function, even
 * if it returned an error! Always use put_device() to give up your
 * reference instead.
 */
int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct class_interface *class_intf;
	int error = -EINVAL;

	dev = get_device(dev);
	if (!dev)
		goto done;

	if (!dev->p) {
		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_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	if (!dev_name(dev)) {
		error = -EINVAL;
		goto name_error;
	}

	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

	parent = get_device(dev->parent);
	setup_parent(dev, parent);

	/* use parent numa_node */
	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 */
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;

	/* notify platform of device entry */
	if (platform_notify)
		platform_notify(dev);

	error = device_create_file(dev, &uevent_attr);
	if (error)
		goto attrError;

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &devt_attr);
		if (error)
			goto ueventattrError;

		error = device_create_sys_dev_entry(dev);
		if (error)
			goto devtattrError;

		devtmpfs_create_node(dev);
	}

	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	device_pm_add(dev);

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysf_add() and before kobject_uevent().
	 */
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_ADD_DEVICE, dev);

	kobject_uevent(&dev->kobj, KOBJ_ADD);
	bus_probe_device(dev);		//设备与设备驱动匹配函数
	if (parent)
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);

	if (dev->class) {
		mutex_lock(&dev->class->p->class_mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,
			       &dev->class->p->klist_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->class_interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->class_mutex);
	}
done:
	put_device(dev);
	return error;
 DPMError:
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	if (MAJOR(dev->devt))
		devtmpfs_delete_node(dev);
	if (MAJOR(dev->devt))
		device_remove_sys_dev_entry(dev);
 devtattrError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &devt_attr);
 ueventattrError:
	device_remove_file(dev, &uevent_attr);
 attrError:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	kobject_del(&dev->kobj);
 Error:
	cleanup_device_parent(dev);
	if (parent)
		put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}

Driver/base/bus.c:bus_probe_device()

/**
 * bus_probe_device - probe drivers for a new device
 * @dev: device to probe
 *
 * - Automatically probe for a driver if the bus allows it.
 */
void bus_probe_device(struct device *dev)
{
	struct bus_type *bus = dev->bus;
	int ret;

	if (bus && bus->p->drivers_autoprobe) {
		ret = device_attach(dev);	//自动将设备与设备驱动进行匹配
		WARN_ON(ret < 0);
	}
}

Driver/base/bus.c:device_attach()

/**
 * 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 driver was found;
 * -ENODEV if the device is not registered.
 *
 * When called for a USB interface, @dev->parent lock must be held.
 */
int device_attach(struct device *dev)
{
	int ret = 0;

	device_lock(dev);
	if (dev->driver) 	//已完成设备与设备驱动的绑定
{
		if (klist_node_attached(&dev->p->knode_driver)) {
			ret = 1;
			goto out_unlock;
		}
		ret = device_bind_driver(dev);
		if (ret == 0)
			ret = 1;
		else {
			dev->driver = NULL;
			ret = 0;
		}
	}
 	else 
{
		pm_runtime_get_noresume(dev);
		//遍历总线链表匹配对应的设备驱动
		ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
		pm_runtime_put_sync(dev);
	}
out_unlock:
	device_unlock(dev);
	return ret;
}

3.1.4 Platform设备的注销

void platform_device_unregister(struct platform_device *pdev);

3.2 Platform驱动的注册与注销

3.2.1 Platform驱动的注册方法

Drivers/base/platform.c:platform_driver_register()

/**
 * platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 */
int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type;			//将驱动挂接在platform总线下
	
	//对驱动相应的函数进行填充
if (drv->probe)
		drv->driver.probe = platform_drv_probe;	
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);		//注册相应的设备驱动
}

Drivers/base/driver.c:driver_register()

/**
 * driver_register - register driver with bus
 * @drv: driver to register
 *
 * We pass off most of the work to the bus_add_driver() call,
 * since most of the things we have to do deal with the bus
 * structures.
 */
int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;		//定义一个设备驱动指针

	BUG_ON(!drv->bus->p);

	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

//比对当前注册的设备驱动名字是否在该总线下已存在相同的设备驱动名字
	other = driver_find(drv->name, drv->bus); 
	if (other) {
		put_driver(other);	//若名字相同则打印错误并退出
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}

//将设备驱动与对应的kobj对象与组织建立关系
	ret = bus_add_driver(drv);
	if (ret)
		return ret;
	ret = driver_add_groups(drv, drv->groups);
	if (ret)
		bus_remove_driver(drv);
	return ret;
}
EXPORT_SYMBOL_GPL(driver_register);

3.2.2 Platform设备驱动与设备的匹配过程

Drivers/base/bus.c:bud_add_driver()

/**
 * bus_add_driver - Add a driver to the bus.
 * @drv: driver.
 */
int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;		//定义bus_type结构体指针
	struct driver_private *priv;	//定义设备驱动私有数据指针
	int error = 0;

	bus = bus_get(drv->bus);	//获取设备驱动的bus
	if (!bus)
		return -EINVAL;

	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		error = -ENOMEM;
		goto out_put_bus;
	}
	klist_init(&priv->klist_devices, NULL, NULL); // 初始化 priv->klist_devices 链表
	priv->driver = drv;		//该指针指向当前设备驱动
	drv->p = priv;

// 设置设备驱动对象的父对象( 也就是指向一个 kset ) 父对象就是
// /sys/bus/bus_type/drivers/ 这个目录对应的对象
	priv->kobj.kset = bus->p->drivers_kset;

	// 添加kobject 对象到目录层次中 就能够
// 在 /sys/bus/bus_type/drivers/ 目录中看到设备驱动对应的文件了
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	if (error)
		goto out_unregister;

	if (drv->bus->p->drivers_autoprobe) {
		error = driver_attach(drv);	//自动匹配设备与设备驱动,依次匹配bus总线下的各个设备
		if (error)
			goto out_unregister;
	}
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	module_add_driver(drv->owner, drv);

	error = driver_create_file(drv, &driver_attr_uevent);	//建立属性文件:Uevent
	if (error) {
		printk(KERN_ERR "%s: uevent attr (%s) failed\n",
			__func__, drv->name);
	}
	error = driver_add_attrs(bus, drv);	// 根据总线的 bus->drv_attrs 来建立属性文件
	if (error) {
		/* How the hell do we get out of this pickle? Give up */
		printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
			__func__, drv->name);
	}

	if (!drv->suppress_bind_attrs) {
		error = add_bind_files(drv);
		if (error) {
			/* Ditto */
			printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
				__func__, drv->name);
		}
	}

	kobject_uevent(&priv->kobj, KOBJ_ADD);
	return 0;

out_unregister:
	kobject_put(&priv->kobj);
	kfree(drv->p);
	drv->p = NULL;
out_put_bus:
	bus_put(bus);
	return error;
}

Drivers/base/dd.c:driver_attach()

/**
 * driver_attach - try to bind driver to devices.
 * @drv: driver.
 *
 * Walk the list of devices that the bus has on it and try to
 * match the driver with each one.  If driver_probe_device()
 * returns 0 and the @dev->driver is set, we've found a
 * compatible pair.
 */
int driver_attach(struct device_driver *drv)
{
	//依次去匹配bus总线下的各个设备
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);

Drivers/base/bus.c:bus_for_each_dev()
/**
 * bus_for_each_dev - device iterator.
 * @bus: bus type.
 * @start: device to start iterating from.
 * @data: data for the callback.
 * @fn: function to be called for each device.
 *
 * Iterate over @bus's list of devices, and call @fn for each,
 * passing it @data. If @start is not NULL, we use that device to
 * begin iterating from.
 *
 * We check the return of @fn each time. If it returns anything
 * other than 0, we break out and return that value.
 *
 * NOTE: The device that returns a non-zero value is not retained
 * in any way, nor is its refcount incremented. If the caller needs
 * to retain this data, it should do so, and increment the reference
 * count in the supplied callback.
 */
int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus)
		return -EINVAL;

	// 这个函数的功能就是将 klist_devices 和 knode_bus填充到 i 变量中
klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	
//依次返回出总线上的各个设备结构体device
	while ((dev = next_device(&i)) && !error)
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}

Drivers/base/bus.c:__driver_attach()

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;

	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */

	if (!driver_match_device(drv, dev))//调用match函数,进行设备驱动与设备的匹配
		return 0;

	if (dev->parent)	/* Needed for USB */
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->driver)
		driver_probe_device(drv, dev);	//调用相关的probe函数
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);

	return 0;
}

Drivers/base/dd.c:driver_probe_device()

/**
 * driver_probe_device - attempt to bind device & driver together
 * @drv: driver to bind a device to
 * @dev: device to try to bind to the driver
 *
 * This function returns -ENODEV if the device is not registered,
 * 1 if the device is bound successfully and 0 otherwise.
 *
 * This function must be called with @dev lock held.  When called for a
 * USB interface, @dev->parent lock must be held as well.
 */
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;

	if (!device_is_registered(dev))
		return -ENODEV;

	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

	pm_runtime_get_noresume(dev);
	pm_runtime_barrier(dev);
	ret = really_probe(dev, drv);//调用设备驱动或总线下的probe函数
	pm_runtime_put_sync(dev);

	return ret;
}

Drivers/base/dd.c:really_probe ()

static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;

	atomic_inc(&probe_count);
	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
		 drv->bus->name, __func__, drv->name, dev_name(dev));
	WARN_ON(!list_empty(&dev->devres_head));

	dev->driver = drv;	//当前设备的驱动指针指向传入的设备驱动
	if (driver_sysfs_add(dev)) {
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}

	if (dev->bus->probe) {	//调用总线下的probe函数
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {	//调用设备驱动下的probe函数
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}

	driver_bound(dev);
	ret = 1;
	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
	goto done;

probe_failed:
	devres_release_all(dev);
	driver_sysfs_remove(dev);
	dev->driver = NULL;

	if (ret != -ENODEV && ret != -ENXIO) {
		/* driver matched but the probe failed */
		printk(KERN_WARNING
		       "%s: probe of %s failed with error %d\n",
		       drv->name, dev_name(dev), ret);
	}
	/*
	 * Ignore errors returned by ->probe so that the next driver can try
	 * its luck.
	 */
	ret = 0;
done:
	atomic_dec(&probe_count);
	wake_up(&probe_waitqueue);
	return ret;
}

3.2.3 Platform驱动的注销方法

Drivers/base/platform.c:platform_driver_unregister()

void platform_driver_unregister(struct platform_driver *drv);

3.3 Platform设备与驱动的匹配

当设备或驱动挂接到platform总线时,该总线的match方法会被调用,及platform_match被调用。
Drivers/base/platform.c:platform_match()

/**
 * platform_match - bind platform device to platform driver.
 * @dev: device.
 * @drv: driver.
 *
 * Platform device IDs are assumed to be encoded like this:
 * "<name><instance>", where <name> is a short description of the type of
 * device, like "pci" or "floppy", and <instance> is the enumerated
 * instance of the device, like '0' or '42'.  Driver IDs are simply
 * "<name>".  So, extract the <name> from the platform_device structure,
 * and compare it against the name of the driver. Return whether they match
 * or not.
 */
//总线下的设备与设备驱动的匹配函数
static int platform_match(struct device *dev, struct device_driver *drv)
{
	//通过device获取platform_device
struct platform_device *pdev = to_platform_device(dev);
	//通过driver获取platform_driver
struct platform_driver *pdrv = to_platform_driver(drv);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)		//如果pdrv中的id_table表存在,则匹配id_table
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	//第二个才匹配设备名与设备驱动名
	return (strcmp(pdev->name, drv->name) == 0);
}

Drivers/base/platform.c:platform_match_id()

static const struct platform_device_id *platform_match_id(
			const struct platform_device_id *id,
			struct platform_device *pdev)
{
	//循环比较id_table数组中各个id的名字是否与pdev->name相同
while (id->name[0]) {
		if (strcmp(pdev->name, id->name) == 0) {
			pdev->id_entry = id;//将匹配成功id赋值给pdev->id_entry
			return id;
		}
		id++;
	}
	return NULL;
}

4 Platform其他API

4.1 Platform Device相关API

extern struct bus_type platform_bus_type;
extern struct device platform_bus;

//设置 platform_device 变量中的archdata指针
extern void arch_setup_pdev_archdata(struct platform_device *);

//获取platform_device设备的资源相关API
extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
extern int platform_get_irq(struct platform_device *, unsigned int);
extern int platform_irq_count(struct platform_device *);
extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, const char *);
extern int platform_get_irq_byname(struct platform_device *, const char *);
extern int platform_add_devices(struct platform_device **, int);

platform_device_alloc ,以name和id为参数,动态分配一个 struct platform_device 变量。
platform_device_add_resources ,向platform device中增加资源描述。
platform_device_add_data ,向platform device中添加自定义的数据(保存在 pdev->dev.platform_data 指针中)。
platform_device_add 、 platform_device_del 、 platform_device_put ,其它操作接口。
4.2	Platform Driver相关API
//主动执行probe动作
extern int __platform_driver_probe(struct platform_driver *driver, int (*probe)(struct platform_device *), struct module *module);

//用来设置或者获取driver保存在device变量中的私有数据
static inline void *platform_get_drvdata(const struct platform_device *pdev)
{
return dev_get_drvdata(&pdev->dev);
}
static inline void platform_set_drvdata(struct platform_device *pdev, void *data)
{
dev_set_drvdata(&pdev->dev, data);
}
4.3	Every platform device/driver
内核启动时,要完成一定的初始化操作之后,才会处理device和driver的注册及probe,因此在这之前,常规的platform设备是无法使用的。但是在Linux中充当console输出的serial设备),所以platform模块提供了一种称作Early platform device/driver的机制,允许驱动开发人员,在开发驱动时,向内核机制提供了如下接口:
//注册一个用于Early device的driver
extern int early_platform_driver_register(struct early_platform_driver *epdrv, char *buf);
//添加一个Early device
extern void early_platform_add_devices(struct platform_device **devs, int num);
//判断指定的device是否是Early device
static inline int is_early_platform_device(struct platform_device *pdev)
{
return !pdev->dev.driver;
}
//将指定class的所有driver注册为Early device driver
extern void early_platform_driver_register_all(char *class_str);
//probe指定class的Early device
extern int early_platform_driver_probe(char *class_str, int nr_probe, int user_only);
//清除所有的Early device/driver
extern void early_platform_cleanup(void);

5 Platform平台驱动使用方法

5.1 Platform_device的注册过程

  • A. 设置好platform_device结构体,并完成初始化工作;
  • B. 将platform_device设备加入到板文件对应的platform设备中;
  • C. 板文件在初始化时会调用platform_add_device()会完成platform设备的注册,完成注册以后将会在/sys/bus/platform/devices目录下会看到dev.name的文件夹

5.2 Platform_driver的注册过程

  • A. 设置好platform_driver结构体,及device_driver结构体;
  • B. 调用platform_driver_register注册设备驱动,完成注册以后再 /sys/bus/platform/drivers目录下会看到 dev.name的文件夹;
  • C. 实现platform_driver的probe、remove等等函数。

5.3 Platform平台驱动使用实例

略。

发布了47 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/u013836909/article/details/95675467