【Linux驱动开发】platform

驱动的分离和分层

  • 为了方便开发,将主机驱动和设备驱动分离开来,主机驱动和设备驱动通过统一的接口访问。
  • 主机驱动由半导体厂商编写,设备驱动由设备器件厂商编写,用户只需提供设备信息。
  • 驱动的分层是为了在不同的层处理不同的内容。
  • Linux采用驱动-总线-设备模型,通过总线匹配驱动和设备
  • 向系统注册一个驱动时,总线在所以设备中查找与驱动匹配的设备。
  • 向系统注册一个设备时,总线在所有驱动中查找与设备匹配的驱动。
  • 总线由Linux内核提供,无需编写;用户需编写驱动和设备程序。
  • 驱动是具体的设备驱动;设备是设备属性信息包括地址、速度等。

platform

Linux提供platform虚拟总线,可以使LCD、RTC等没有总线概念的外设中使用驱动-总线-设备模型。

platform总线

Linux定义bus_type结构体表示总线,include/linux/device.h。

struct bus_type {
	const char		*name;
	const char		*dev_name;
	struct device		*dev_root;
	struct device_attribute	*dev_attrs;	/* use dev_groups instead */
	const struct attribute_group **bus_groups;
	const struct attribute_group **dev_groups;
	const struct attribute_group **drv_groups;

	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 (*online)(struct device *dev);
	int (*offline)(struct device *dev);

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

	const struct dev_pm_ops *pm;

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;
	struct lock_class_key lock_key;
};

bus_type结构体中,match函数用于驱动和设备之间的匹配,每一条总线都必须实现match函数。match函数的dev和drv参数分别为设备和驱动的类型。 驱动和设备匹配方法有四种,常用设备树OF类型匹配和字段匹配方法,platform_match匹配函数在drivers/base/platform.c。

static int platform_match(struct device *dev, 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)
		return !strcmp(pdev->driver_override, drv->name);

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

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		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);
}
  • 第一种匹配方法,OF类型匹配,设备树采用的匹配方法,使用of_driver_match_device函数。在device_driver设备驱动结构体中of_match_table成员变量保存compatible匹配表,设备树中的每个设备节点的compatible属性都会和of_match_table匹配表中成员比较,如果相同表示匹配,probe函数执行。
  • 第二种匹配方法,ACPI匹配。
  • 第三种匹配方法,id_table匹配,根据platform_driver结构体id_table成员变量中id信息进行匹配。
  • 第四章匹配方法,name字段匹配,比较驱动和设备中的name字段,相同表示匹配成功。

platform驱动

platform只是为了驱动分离和分层提出的一种框架,platform驱动的具体实现还需要字符设备驱动、块设备驱动和网络设备驱动。

Linux定义platform_driver结构体表示platform驱动,include/linux/platform_device.h。

/* 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;
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};

/* include/linux/device.h */
struct device_driver {
	const char		*name;
	struct bus_type		*bus;

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */

	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;

	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 attribute_group **groups;

	const struct dev_pm_ops *pm;

	struct driver_private *p;
};

/* include/linux/mod_devicetable.h */
struct platform_device_id {
	char name[PLATFORM_NAME_SIZE];
	kernel_ulong_t driver_data;
};

struct of_device_id {
	char	name[32];
	char	type[32];
	char	compatible[128];
	const void *data;
};
  •  驱动和设备匹配成功后,执行probe函数,用户编写probe函数。
  • driver成员变量是device_driver结构体变量,platform_driver继承device_driver这个“基类”。
  • id_table是个数组,每个元素类型为platform_device_id。
  • device_driver结构体中of_match_table是个数组,是使用设备树时驱动的匹配表,每个元素类型为of_device_id。
  • of_device_id结构体中的compatible变量,设备树通过设备节点的compatible属性和of_match_table中的每个项目的compatible成员变量比较,相同表示匹配成功。

platform驱动首先定义一个platform_driver结构体变量,然后使用platform_driver_register函数注册platform驱动。

int platform_driver_register(struct platform_driver *driver);
  • driver:要注册的platform驱动。
  • 返回值:0,成功;负值,失败。 

使用platform_driver_unregister函数卸载platform驱动。

void platform_driver_unregister(struct platform_driver *drv);
  • driver:要卸载的platform驱动。
  • 返回值:无。

platform驱动框架

/* 设备结构体 */
struct xxx_dev{
    struct cdev cdev;
    /* ... */
};

struct xxx_dev xxxdev;

/* 字符设备操作集合 */
static struct file_operation xxx_fops = {
    .owner = THIS_MODULE;
    /* ... */
};

/* probe函数在驱动和设备匹配后执行 */
static int xxx_probe(struct platform_device *dev){
    /* 注册字符设备驱动代码*/
    /* ... */
    cdev_init(&xxxdev.cdev, &xxx_fops);

    return 0;
}

/* 关闭platform驱动后执行 */
static int xxx_remove(struct platform_device *dev){
    /* 卸载字符设备驱动代码*/
    /* ... */
    cdev_del(&xxxdev.cdev);

    return 0;
}

/* 匹配列表 */
static const struct of_device_id xxx_of_match[] = {
    {.compatible = "xxx-gpio"},
    { /* Sentinel */ },      /* 最后一个匹配项为空 */
};

/* platform驱动结构体 */
static struct platform_driver xxx_driver = {
    .driver = {
        .name = "xxx",                  /* 无设备树匹配 */
        .of_match_table = xxx_of_match, /* 设备树匹配 */
    },
    .probe = xxx_probe,
    .remove = xxx_remove,
};

/* 驱动模块加载 */
static int __init xxx_driver_init(void){
    return platform_driver_register(&xxx_driver); /* 注册platform驱动 */
}
/* 驱动模块卸载 */
static void __exit xxx_driver_exit(void){
    platform_driver_unregister(&xxx_driver);      /* 卸载platform驱动 */
}

module_init(xxx_driver_init);
module_exit(xxx_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XXXUUUXXX");
  • xxx_probe:将放在模块加载init函数中的代码如注册字符设备驱动、添加cdev、创建类和设备等都放在probe函数内,当驱动和设备匹配成功后执行probe函数。
  • xxx_remove:将放在模块卸载exit函数中的代码如注销设备号、删除cdev等都放在remove函数内。
  • xxx_of_match:使用设备树使,匹配节点的compatible属性,of_device_id最后一个匹配项必须为空
  • xxx_driver:name属性用于无设备树匹配方法,匹配驱动和设备的name字段是否相同。of_match_table属性用于设备树匹配方法。

platform设备

  • 使用设备树表示设备,如何Linux内核支持设备树,使用设备树描述设备。
  • Linux定义platform_device结构体表示platform设备。include/linux/platform_device.h。
struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u32		num_resources;
	struct resource	*resource;

	const struct platform_device_id	*id_entry;
	char *driver_override; /* Driver name to force a match */

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

	/* arch specific additions */
	struct pdev_archdata	archdata;
};
  • name:设备名字,与驱动的name字段进行匹配。
  • num_resources:资源数量。
  • resource:资源设备信息,包括外设寄存器等。

resource结构体表示资源,include/linux/ioport.h。

struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	struct resource *parent, *sibling, *child;
};
  • start:资源起始信息(地址)。
  • end:资源终止信息(地址)。
  • name:资源名字。
  • flag:资源类型。

resource资源类型flag,在include/linux/ioport.h中用宏定义表示。

/*
 * IO resources have these defined flags.
 */
#define IORESOURCE_BITS		0x000000ff	/* Bus-specific bits */

#define IORESOURCE_TYPE_BITS	0x00001f00	/* Resource type */
#define IORESOURCE_IO		0x00000100	/* PCI/ISA I/O ports */
#define IORESOURCE_MEM		0x00000200
#define IORESOURCE_REG		0x00000300	/* Register offsets */
#define IORESOURCE_IRQ		0x00000400
#define IORESOURCE_DMA		0x00000800
#define IORESOURCE_BUS		0x00001000

#define IORESOURCE_PREFETCH	0x00002000	/* No side effects */
#define IORESOURCE_READONLY	0x00004000
#define IORESOURCE_CACHEABLE	0x00008000
#define IORESOURCE_RANGELENGTH	0x00010000
#define IORESOURCE_SHADOWABLE	0x00020000

#define IORESOURCE_SIZEALIGN	0x00040000	/* size indicates alignment */
#define IORESOURCE_STARTALIGN	0x00080000	/* start field is alignment */

#define IORESOURCE_MEM_64	0x00100000
#define IORESOURCE_WINDOW	0x00200000	/* forwarded by bridge */
#define IORESOURCE_MUXED	0x00400000	/* Resource is software muxed */

#define IORESOURCE_EXCLUSIVE	0x08000000	/* Userland may not map this resource */
#define IORESOURCE_DISABLED	0x10000000
#define IORESOURCE_UNSET	0x20000000	/* No address assigned yet */
#define IORESOURCE_AUTO		0x40000000
#define IORESOURCE_BUSY		0x80000000	/* Driver has marked this resource busy */

/* PnP IRQ specific bits (IORESOURCE_BITS) */
#define IORESOURCE_IRQ_HIGHEDGE		(1<<0)
#define IORESOURCE_IRQ_LOWEDGE		(1<<1)
#define IORESOURCE_IRQ_HIGHLEVEL	(1<<2)
#define IORESOURCE_IRQ_LOWLEVEL		(1<<3)
#define IORESOURCE_IRQ_SHAREABLE	(1<<4)
#define IORESOURCE_IRQ_OPTIONAL 	(1<<5)

/* PnP DMA specific bits (IORESOURCE_BITS) */
#define IORESOURCE_DMA_TYPE_MASK	(3<<0)
#define IORESOURCE_DMA_8BIT		(0<<0)
#define IORESOURCE_DMA_8AND16BIT	(1<<0)
#define IORESOURCE_DMA_16BIT		(2<<0)

#define IORESOURCE_DMA_MASTER		(1<<2)
#define IORESOURCE_DMA_BYTE		(1<<3)
#define IORESOURCE_DMA_WORD		(1<<4)

#define IORESOURCE_DMA_SPEED_MASK	(3<<6)
#define IORESOURCE_DMA_COMPATIBLE	(0<<6)
#define IORESOURCE_DMA_TYPEA		(1<<6)
#define IORESOURCE_DMA_TYPEB		(2<<6)
#define IORESOURCE_DMA_TYPEF		(3<<6)

/* PnP memory I/O specific bits (IORESOURCE_BITS) */
#define IORESOURCE_MEM_WRITEABLE	(1<<0)	/* dup: IORESOURCE_READONLY */
#define IORESOURCE_MEM_CACHEABLE	(1<<1)	/* dup: IORESOURCE_CACHEABLE */
#define IORESOURCE_MEM_RANGELENGTH	(1<<2)	/* dup: IORESOURCE_RANGELENGTH */
#define IORESOURCE_MEM_TYPE_MASK	(3<<3)
#define IORESOURCE_MEM_8BIT		(0<<3)
#define IORESOURCE_MEM_16BIT		(1<<3)
#define IORESOURCE_MEM_8AND16BIT	(2<<3)
#define IORESOURCE_MEM_32BIT		(3<<3)
#define IORESOURCE_MEM_SHADOWABLE	(1<<5)	/* dup: IORESOURCE_SHADOWABLE */
#define IORESOURCE_MEM_EXPANSIONROM	(1<<6)

/* PnP I/O specific bits (IORESOURCE_BITS) */
#define IORESOURCE_IO_16BIT_ADDR	(1<<0)
#define IORESOURCE_IO_FIXED		(1<<1)

/* PCI ROM control bits (IORESOURCE_BITS) */
#define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
#define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
#define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
#define IORESOURCE_ROM_BIOS_COPY	(1<<3)	/* ROM is BIOS copy, resource field overlaid */

/* PCI control bits.  Shares IORESOURCE_BITS with above PCI ROM.  */
#define IORESOURCE_PCI_FIXED		(1<<4)	/* Do not move resource */

在不支持设备树的Linux版本,使用platform_device表示设备信息,使用platform_device_register函数将platform设备信息注册到Linux内核。

int platform_device_register(struct platform_device *pdev);
  • pdev:要注册的platform设备。
  • 返回值:0,成功;负值,失败。

使用platform_device_unregister函数注销platform设备。

void platform_device_unregister(struct platform_device *pdev);
  • pdev:要注销的platform设备。
  • 返回值:无。

platform设备框架—无设备树

/* 寄存器地址定义外设寄存器基地址 */
#define PERIPH1_REGISTER_BASE    (0x20000000)
#define PERIPH2_REGISTER_BASE    (0x020e0068)
#define REGISTER_LEN             4

/* 资源 */
static struct resource xxx_resources[] = {
    [0] = {
        .start = PERIPH1_REGISTER_BASE,
        .end   = (PERIPH1_REGISTER_BASE + REGISTER_LEN - 1),
        .flag  = IORESOURCE_MEM,
    },
    [1] = {
        .start = PERIPH2_REGISTER_BASE,
        .end   = (PERIPH2_REGISTER_BASE + REGISTER_LEN - 1),
        .flag  = IORESOURCE_MEM,
    },
};

/* platform设备 */
static struct platform_device xxx_device = {
    .name = "xxx-gpio",
    .id = -1,
    .num_resources = ARRAY_SIZE(xxx_resources),
    .resource = xxx_resources,  
};

/* 设备模块加载 */
static int __init xxx_device_init(void){
    return platform_device_register(&xxx_device);
}

/* 设备模块卸载 */
static void __exit xxx_device_exit(void){
    platform_device_unregister(&xxx_device);
}

module_init(xxx_device_init);
module_exit(xxx_device_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XXXUUUXXX");
  • xxx_resources:设备资源,包括外设起始信息、终止信息、资源类型,此处为内存类型。资源获取使用platform_get_resource函数。
  • xxx_device:platform设备结构体变量,name字段和驱动中的name字段相同表示匹配成功,使用ARRAY_SIZE获取数组元素个数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奶油芝士汉堡包

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值