# 前置知识
总线驱动模型简介:
总线是处理器与一个或者多个设备之间的通道,在设备模型中,所有的设备都是通过总线相连,当然也包括虚拟的 platform 平台总线。
总线驱动模型中有三要素:
1. 总线
/**
* struct bus_type - The bus type of the device
*
* @name: The name of the bus.
* @dev_name: Used for subsystems to enumerate devices like ("foo%u", dev->id).
* @dev_root: Default device to use as the parent.
* @dev_attrs: Default attributes of the devices on the bus.
* @bus_groups: Default attributes of the bus.
* @dev_groups: Default attributes of the devices on the bus.
* @drv_groups: 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.
*
* @online: Called to put the device back online (after offlining it).
* @offline: Called to put the device offline for hot-removal. May fail.
*
* @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.
* @iommu_ops: IOMMU specific operations for this bus, used to attach IOMMU
* driver implementations to a bus and allow the driver to do
* bus-specific setup
* @p: The private data of the driver core, only the driver core can
* touch this.
* @lock_key: Lock class key for use by the lock validator
*
* 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;
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;
};
API:
extern int __must_check bus_register(struct bus_type *bus);
extern void bus_unregister(struct bus_type *bus);
平台总线定义:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.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);
of_platform_register_reconfig_notifier();
return error;
}
2. 总线设备
struct platform_device {
const char *name; /* 名字 */
int id; /* 用于标识该设备的ID */
bool id_auto; /* 指示在注册设备时,是否自动赋予ID值 */
struct device dev; /* 真正的设备(Platform设备只是一个特殊的设备,因此其核心逻辑还是由底层的模块实现) */
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; /* 私有数据 */
};
struct resource {
resource_size_t start; /* 起始地址 */
resource_size_t end; /* 结束地址 */
const char *name; /* 资源名字 */
unsigned long flags; /* 资源标识 */
struct resource *parent, *sibling, *child;
};
API:
/* 注册平台设备 */
int platform_device_register(struct platform_device *);
/* 注销平台设备 */
void platform_device_unregister(struct platform_device *);
/* 设置platform_device变量中的archdata指针 */
void arch_setup_pdev_archdata(struct platform_device *);
/* 通过资源类型获取platform_device变量中的resource信息 */
struct resource *platform_get_resource(struct platform_device *,
unsigned int, unsigned int);
/* 通过资源名字获取platform_device变量中的resource信息 */
struct resource *platform_get_resource_byname(struct platform_device *,
unsigned int,
const char *);
3. 总线驱动
struct platform_driver {
int (*probe)(struct platform_device *); /* 当驱动和硬件信息匹配成功之后,就会调用probe函数,驱动所有的资源的注册和初始化全部放在probe函数中 */
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; /* 内核维护的所有的驱动必须包含该成员,通常driver->name用于和设备进行匹配 */
const struct platform_device_id *id_table; /* 往往一个驱动可能能同时支持多个硬件,这些硬件的名字都放在该结构体数组中 */
bool prevent_deferred_probe;
};
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;
};
API:
/*
* use a macro to avoid include chaining to get THIS_MODULE
*/
/* 注册设备 */
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE)
extern int __platform_driver_register(struct platform_driver *,
struct module *);
/* 注销设备 */
extern void platform_driver_unregister(struct platform_driver *);
/* non-hotpluggable platform devices may use this so that probe() and
* its support may live in __init sections, conserving runtime memory.
*/
#define platform_driver_probe(drv, probe) \
__platform_driver_probe(drv, probe, THIS_MODULE)
extern int __platform_driver_probe(struct platform_driver *driver,
int (*probe)(struct platform_device *), struct module *module);
/* 获取设备私有资源 */
static inline void *platform_get_drvdata(const struct platform_device *pdev)
{
return dev_get_drvdata(&pdev->dev);
}
设备与驱动匹配过程
来自:https://blog.csdn.net/qq_16504163/article/details/118562670
# 示例代码
platform_device:
#include <linux/init.h>
#include <linux/file.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#define CCM_CCGR1_ADDR 0x20C406C
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03_ADDR 0x20E0068
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03_ADDR 0x20E02F4
#define GPIO1_BASE_ADDR 0x209C000
static struct resource atk_led_resource[] = {
[0] = {
.start = CCM_CCGR1_ADDR,
.end = CCM_CCGR1_ADDR + 4,
.name = "led_clock",
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03_ADDR,
.end = IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03_ADDR + 4,
.name = "led_mux_ctrl",
.flags = IORESOURCE_MEM,
},
[2] = {
.start = IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03_ADDR,
.end = IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03_ADDR + 4,
.name = "led_pad_ctrl",
.flags = IORESOURCE_MEM,
},
[3] = {
.start = GPIO1_BASE_ADDR,
.end = GPIO1_BASE_ADDR + 4,
.name = "led_gpio_ctrl",
.flags = IORESOURCE_MEM,
}
};
static void atk_led_release(struct device *dev)
{
}
static struct platform_device atk_led_dev = {
.name = "atk_led",
.id = -1,
.resource = atk_led_resource,
.num_resources = ARRAY_SIZE(atk_led_resource),
.dev = {
.release = atk_led_release,
},
};
static int __init led_device_init(void)
{
int err;
err = platform_device_register(&atk_led_dev);
return 0;
}
static void __exit led_device_exit(void)
{
platform_device_unregister(&atk_led_dev);
}
module_init(led_device_init);
module_exit(led_device_exit);
MODULE_LICENSE("GPL");
platform_driver:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/platform_device.h>
typedef struct
{
volatile uint32_t GPIO_DR;
volatile uint32_t GPIO_GDIR;
volatile uint32_t GPIO_PSR;
volatile uint32_t GPIO_ICR1;
volatile uint32_t GPIO_ICR2;
volatile uint32_t GPIO_IMR;
volatile uint32_t GPIO_ISR;
volatile uint32_t GPIO_EDGE_SEL;
}GPIO_TypeDef;
static GPIO_TypeDef *GPIO;
static volatile uint32_t *CCM_CCGR;
static volatile uint32_t *IOMUXC_SW_MUX_CTL_PAD;
static volatile uint32_t *IOMUXC_SW_PAD_CTL_PAD;
struct pri_led_TypeDef
{
char drv_name[50]; /* 驱动名称 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
dev_t devt; /* 设备号 */
struct device *device; /* 设备 */
char device_name[50]; /* 设备名称 */
struct class *class; /* 类 */
char class_name[50]; /* 类名称 */
};
static struct pri_led_TypeDef pri_led = {
.drv_name = "led_drv",
.major = 0,
.minor = 0,
.devt = 0,
.device = NULL,
.device_name = "led_dev",
.class = NULL,
.class_name = "led_class",
};
static int led_open(struct inode *inode, struct file *file)
{
uint32_t val;
/* 使能GPIO1时钟 */
*CCM_CCGR |= (3 << 26);
/* 设置IO复用 */
val = *IOMUXC_SW_MUX_CTL_PAD;
val &= ~(0x0F);
val |= 5;
*IOMUXC_SW_MUX_CTL_PAD = val;
/* 设置IO属性 */
/* 设置IO方向,设置GPIO1_IO03为输出 */
GPIO->GPIO_GDIR |= (1 << 3);
return 0;
}
static int led_release(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t led_write(struct file *file, const char __user *buff, size_t size, loff_t *ppos)
{
uint8_t status;
int err;
err = copy_from_user(&status, buff, 1);
if(status == 1)
{
GPIO->GPIO_DR &= ~(1<<3);
}
else
{
GPIO->GPIO_DR |= (1<<3);
}
return 1;
}
static const struct file_operations led_op = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
.write = led_write,
};
static int atk_led_drv_probe(struct platform_device *dev)
{
struct resource *res;
int err;
printk("atk_led_drv_probe\r\n");
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
CCM_CCGR = ioremap(res->start, (res->end - res->start));
res = platform_get_resource(dev, IORESOURCE_MEM, 1);
IOMUXC_SW_MUX_CTL_PAD = ioremap(res->start, (res->end - res->start));
res = platform_get_resource(dev, IORESOURCE_MEM, 2);
IOMUXC_SW_PAD_CTL_PAD = ioremap(res->start, (res->end - res->start));
res = platform_get_resource(dev, IORESOURCE_MEM, 3);
GPIO = ioremap(res->start, sizeof(GPIO_TypeDef));
pri_led.major = register_chrdev(0, pri_led.drv_name, &led_op);
pri_led.devt = MKDEV(pri_led.major, pri_led.minor);
pri_led.class = class_create(THIS_MODULE, pri_led.class_name);
if(IS_ERR(pri_led.class))
{
printk("class_create error\r\n");
err = PTR_ERR(pri_led.class);
goto err_class_create_out;
}
pri_led.device = device_create(pri_led.class, NULL, pri_led.devt, NULL, pri_led.device_name);
if(IS_ERR(pri_led.device))
{
printk("device_create error\r\n");
err = PTR_ERR(pri_led.device);
goto err_device_create_out;
}
return 0;
err_device_create_out:
class_destroy(pri_led.class);
err_class_create_out:
unregister_chrdev(pri_led.major, pri_led.drv_name);
return err;
}
static int atk_led_drv_remove(struct platform_device *dev)
{
printk("atk_led_drv_remove\r\n");
iounmap(GPIO);
iounmap(CCM_CCGR);
iounmap(IOMUXC_SW_MUX_CTL_PAD);
iounmap(IOMUXC_SW_PAD_CTL_PAD);
device_destroy(pri_led.class, pri_led.devt);
class_destroy(pri_led.class);
unregister_chrdev(pri_led.major, pri_led.drv_name);
return 0;
}
static struct platform_driver atk_led_driver = {
.probe = atk_led_drv_probe,
.remove = atk_led_drv_remove,
.driver = {
.name = "atk_led",
},
};
static int __init led_driver_init(void)
{
int err;
err = platform_driver_register(&atk_led_driver);
return 0;
}
static void __exit led_driver_exit(void)
{
platform_driver_unregister(&atk_led_driver);
}
module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
# 补充知识
1. platform_match 匹配规程
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);
}
- 最先比较:platform_device.driver_override 和 platform_driver.driver.name,可以设置 platform_device 的 driver_override,强制选择某个 platform_driver
- 然后比较:platform_device. name 和 platform_driver.id_table[i].name,Platform_driver.id_table 是“platform_device_id”指针,表示该 drv 支持若干个 device,它里面列出了各个 device 的{.name, .driver_data},其中的“ name”表示该drv 支持的设备的名字, driver_data 是些提供给该 device 的私有数据
- 最后比较:platform_device.name 和 platform_driver.driver.name,platform_driver.id_table 可能为空,这时可以根据 platform_driver.driver.name 来寻找同名的 platform_device
2. struct platform_device 中 id 的作用
switch (pdev->id) {
default:
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
break;
case PLATFORM_DEVID_NONE: /* -1 */
dev_set_name(&pdev->dev, "%s", pdev->name);
break;
case PLATFORM_DEVID_AUTO: /* -2 */
/*
* Automatically allocated device ID. We mark it as such so
* that we remember it must be freed, and we append a suffix
* to avoid namespace collision with explicit IDs.
*/
ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto err_out;
pdev->id = ret;
pdev->id_auto = true;
dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
break;
}
- id = -1 时,名称直接为名字
- id = -2时,名称为自动获取的id加上.auto
- 其他为名称加上id