【Linux】platform设备驱动

总线、设备、驱动

概述

设备:代表一个物理硬件or虚拟出来的硬件,他可以是连接到SoC上的具体硬件,如鼠标、键盘等;也可以是一个抽象出来的硬件,例如CLK PIN_CTL这些,SoC内部提供的功能,板级找不到对应的物理实体,通常会被归纳到platform bus下

驱动: 用来控制和管理特定硬件一系列方法,例如初始化probe、移除remove、低功耗suspend & resume

总线: 物理层面上,总线是CPU和各个组件数据交换的通道,例如APB总线,AXI总线;或者SoC和外设之间数据交换的通道,IIC总线、SPI总线。Linux内部,总线被抽象成一个软件实体。负责管理挂在总线上的驱动和设备。

类(Class):一系列具有相同功能的设备

平台设备驱动

设备注册

设备注册的两种方式
传统的注册方式
  • 调用platform的注册接口
#ifndef DEVICE_REGISTERED_IN_FDT
static void plat_dev_release(struct device *dev)
{
	if (!dev)
		return;

	pr_info("%s[%d]: %s......\n", __func__, __LINE__, dev_name(dev));
}
/*设备注册的第一种方式,但是当前已经不在推荐了*/
struct platform_device plat_dev = {
	.name = "tdev",
	.id = -1,
	.dev = {
		.release = plat_dev_release,
	},
};
#endif

ret = platform_device_register(&plat_dev);
platform_device_unregister(&plat_dev);

早在Kernel2.6时期,Linus有一次著名的发飙,“this whole ARM thing is a f*cking pain in the ass”。哈哈哈,如果每个设备都要使用这种方式,那么每个平台都会有很多重复的代码,所以2.6之后,社区比较推崇设备树的注册方式。

用设备树描述device
  • 在设备树中新建一个节点,我这里写的比较简单,只为match准备了一个compatible字段
wildgoose_test_dev@0 {
        compatible = "wildgoose,tdev";
};
设备注册的过程
platform_device
struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u64		platform_dma_mask;
	struct device_dma_parameters dma_parms;
	u32		num_resources;
	struct resource	*resource;
	const struct platform_device_id	*id_entry;
	const char *driver_override;
	struct mfd_cell *mfd_cell;
	struct pdev_archdata	archdata;
};
platform_device_register
int platform_device_register(struct platform_device *pdev)
{
	device_initialize(&pdev->dev);					    //初始化struct device结构体
	setup_pdev_dma_masks(pdev);									//set default dma masks 
	return platform_device_add(pdev);
}

platform_device
int platform_device_add(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	u32 i;
	int ret;

	if (!dev->parent)
		dev->parent = &platform_bus;										//未指定父设备,父设备就是platform_bus
	dev->bus = &platform_bus_type;										//设备所属总线:platform_bus_type
	switch (pdev->id) {																//set device name, pdev->dev.kobj.name
	default:																					//默认的命名方式
		dev_set_name(dev, "%s.%d", pdev->name,  pdev->id);
		break;
	case PLATFORM_DEVID_NONE:													//pdev->id == -1
		dev_set_name(dev, "%s", pdev->name);
		break;
	case PLATFORM_DEVID_AUTO:													//pdev->id = -2,自动申请一个设备id
		ret = ida_alloc(&platform_devid_ida, GFP_KERNEL);
		if (ret < 0)
			return ret;
		pdev->id = ret;
		pdev->id_auto = true;
		dev_set_name(dev, "%s.%d.auto", pdev->name, pdev->id);
		break;
	}

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

		if (r->name == NULL)
			r->name = dev_name(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) {
			ret = insert_resource(p, r);
			if (ret) {
				dev_err(dev, "failed to claim resource %d: %pR\n", i, r);
				goto failed;
			}
		}
	}

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

	ret = device_add(dev);															//添加设备
	if (ret)
		goto failed;

	return 0;
failed:
	......
	return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);

device_add
int device_add(struct device *dev)
{
	struct subsys_private *sp;
	struct device *parent;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;
	struct kobject *glue_dir = NULL;

	dev = get_device(dev);

	if (!dev->p) {																			//设备私有数据
		error = device_private_init(dev);
	}

	if (dev->init_name) {											        	//如果dev有init_name
		error = dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	if (dev_name(dev))													    		//device的命名
		error = 0;
	/* subsystems can specify simple device enumeration */
	else if (dev->bus && dev->bus->dev_name)
		error = dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
	else
		error = -EINVAL;
	if (error)
		goto name_error;

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

	parent = get_device(dev->parent);														//父设备
	kobj = get_device_parent(dev, parent);							        //父设备的kobj
	if (IS_ERR(kobj)) {
		error = PTR_ERR(kobj);
		goto parent_error;
	}
	if (kobj)
		dev->kobj.parent = kobj;

	/* use parent numa_node */
	if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
		set_dev_node(dev, dev_to_node(parent));

	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);				//设备的尽头,kobj
	if (error) {
		glue_dir = kobj;
		goto Error;
	}

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

	error = device_add_class_symlinks(dev);												//class下的链接
	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);

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

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

		devtmpfs_create_node(dev);															//dev路径下设备节点的创建
	}

	bus_notify(dev, BUS_NOTIFY_ADD_DEVICE);
	kobject_uevent(&dev->kobj, KOBJ_ADD);

	if (dev->fwnode && !dev->fwnode->dev) {
		dev->fwnode->dev = dev;
		fw_devlink_link_device(dev);
	}

	bus_probe_device(dev);																	//寻找匹配的driver,match的过程

	if (dev->fwnode && fw_devlink_drv_reg_done && !dev->can_match)
		fw_devlink_unblock_consumers(dev);

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

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

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf, &sp->interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev);
		mutex_unlock(&sp->mutex);
		subsys_put(sp);
	}
done:
	put_device(dev);
	return error;
......
}
EXPORT_SYMBOL_GPL(device_add);

驱动注册

int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;

	return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(__platform_driver_register);

int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	if (!bus_is_registered(drv->bus)) {								//驱动注册之前,要确保总线已经被注册
		.......
		return -EINVAL;
	}
  ......

	ret = bus_add_driver(drv);												//总线上添加driver,同时为driver匹配device

	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
		bus_remove_driver(drv);
		return ret;
	}
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);
	deferred_probe_extend_timeout();

	return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
int bus_add_driver(struct device_driver *drv)
{
	struct subsys_private *sp = bus_to_subsys(drv->bus);
	struct driver_private *priv;
	int error = 0;

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);

	klist_init(&priv->klist_devices, NULL, NULL);
	priv->driver = drv;
	drv->p = priv;
	priv->kobj.kset = sp->drivers_kset;
	error = (&priv->kobj, &driver_ktype, NULL, "%s", drv->name);

	klist_add_tail(&priv->knode_bus, &sp->klist_drivers);
	if (sp->drivers_autoprobe) {
		error = driver_attach(drv);
		if (error)
			goto out_del_list;
	}
	error = module_add_driver(drv->owner, drv);
	if (error) {
		printk(KERN_ERR "%s: failed to create module links for %s\n",
			__func__, drv->name);
		goto out_detach;
	}

	error = driver_create_file(drv, &driver_attr_uevent);
	error = driver_add_groups(drv, sp->bus->drv_groups);

	if (!drv->suppress_bind_attrs) {
		error = add_bind_files(drv);
	}

	return 0;

	//error handler
  ......
}

设备和驱动匹配的过程
  • 每当有设备注册进来,或者驱动注册进来,都会有device和driver match的过程
//驱动注册
__platform_driver_register
  --->driver_register
  	--->bus_add_driver
  		--->driver_attach
  			-->bus_for_each_dev(drv->bus, NULL, (void *)drv, __driver_attach);
					-->driver_match_device
            -->drv->bus->match ? drv->bus->match(dev, drv) : 1;
//设备注册
platform_device_register
  --->platform_device_add
  	--->device_add
  		--->bus_probe_device
  			--->device_initial_probe
  				--->__device_attach
  					--->ret = bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver);
							--->driver_match_device
                --->drv->bus->match ? drv->bus->match(dev, drv) : 1; 

殊途同归,无论是设备先挂载到总线上,还是驱动先注册到总线,都是调用platform_bus_type.match接口

static int platform_match(struct device *dev, const struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)								//优先使用driver_override匹配应用
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))		//driver_match_table匹配设备
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))	//ACPI的设备匹配方式
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}
  • driver_override: 常用于PCI设备,直接为device指定驱动
  • of_driver_match_device: 通过设备树节点和driver->of_match_table匹配
  • acpi_driver_match_device:没用过
  • platform_match_id: 通过platform_device_id.id_tabledevice->name匹配
  • devcie->namedriver->name进行匹配

Test Code

#include "linux/stddef.h"
#include "linux/types.h"
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>

#define DEVICE_REGISTERED_IN_FDT
/************************************************************************************/
static const struct of_device_id wildgoose_tdev_of_match[] = {
	{ .compatible = "wildgoose,tdev" },
	{ }
};

static int plat_drv_probe(struct platform_device *dev)
{
	pr_info("%s[%d]:......\n", __func__, __LINE__);

	return 0;
}

static void plat_drv_remove(struct platform_device *dev)
{
	pr_info("%s[%d]:......\n", __func__, __LINE__);	
}

static struct platform_driver plat_dev_drv_test = {
	.driver = {
		.name = "tdev",
		.of_match_table = wildgoose_tdev_of_match,
	},

	.probe = plat_drv_probe,
	.remove = plat_drv_remove,
	.shutdown = NULL,
	.suspend = NULL,
	.resume = NULL,

};

#ifndef DEVICE_REGISTERED_IN_FDT
static void plat_dev_release(struct device *dev)
{
	if (!dev)
		return;

	pr_info("%s[%d]: %s......\n", __func__, __LINE__, dev_name(dev));
}
/*设备注册的第一种方式,但是当前已经不在推荐了*/
struct platform_device plat_dev = {
	.name = "tdev",
	.id = -1,
	.dev = {
		.release = plat_dev_release,
	},
};
#endif
/*设备注册的第二种方式,通过设备树注册设备*/

static int __init plat_dev_drv_test_init(void)
{
#ifndef DEVICE_REGISTERED_IN_FDT
	int ret;
	ret = platform_device_register(&plat_dev);
	if (ret)
		return ret;
#endif
	return platform_driver_register(&plat_dev_drv_test);
}

static void __exit plat_dev_drv_test_exit(void)
{
#ifndef DEVICE_REGISTERED_IN_FDT
	platform_device_unregister(&plat_dev);
#endif
	return platform_driver_unregister(&plat_dev_drv_test);
}

module_init(plat_dev_drv_test_init);
module_exit(plat_dev_drv_test_exit);

MODULE_DESCRIPTION("Platform Device Driver Test");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("WildGoose");

测试结果

~ # ls /sys/devices/platform/ | grep "wild"
wildgoose_test_dev@0
~ # ls /sys/devices/platform/wildgoose_test_dev@0/ -al
total 0
drwxr-xr-x    3 0        0                0 Jun  8 11:07 .
drwxr-xr-x   45 0        0                0 Jun  8 11:07 ..
lrwxrwxrwx    1 0        0                0 Jun  8 11:07 driver -> ../../../bus/platform/drivers/tdev
-rw-r--r--    1 0        0             4096 Jun  8 11:07 driver_override
-r--r--r--    1 0        0             4096 Jun  8 11:07 modalias
lrwxrwxrwx    1 0        0                0 Jun  8 11:07 of_node -> ../../../firmware/devicetree/base/wildgoose_test_dev@0
drwxr-xr-x    2 0        0                0 Jun  8 11:07 power
lrwxrwxrwx    1 0        0                0 Jun  8 11:07 subsystem -> ../../../bus/platform
-rw-r--r--    1 0        0             4096 Jun  8 11:07 uevent
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值