linux随笔记 - platform设备驱动及总线

 在SOC中存在I2C、SPI、USB 等总线模型。通过总线来将驱动和设备分开。形成【驱动】-【z总线模型】-【设备】

在SOC中不存在这个外设的总线模型时,就引出了platform总线模型。同样的也是将具体的驱动和控制的设备对象分开。

include/linux/device.h中,bus_type便是这个总线模型,通过其中的match函数对驱动和设备进行匹配。

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;
};

device注册和注销/driver注册和注销:

//注册和注销设备,如果是从驱动获取设备资源,就需要注册该部分
//如果是从设备树获取资源,那么使用对应的of函数从设备树获取对应的资源即可。match函数通过注册的driver和设备树中compatible比较获取资源。
static struct platform_device led_device = {
	.name = "_ledxx",
	.id = -1,
	.dev = {
		.release = &led_release,
	},
    .num_resources = ...,
    .resource = ...,
};
platform_device_register(&led_device );
platform_device_unregister(&led_device );

//注册和注销驱动
static struct platform_driver led_driver= 
{
    .drver = {
        .name = "_ledxx",
    }
    .probe = xxx_probe,//probe函数指针
    .remove= xxx_remove,//remove函数指针
}

platform_driver_register(&led_driver);
platform_driver_unregister(&led_driver);

其中在注册设或则是注册驱动的时候,最终会调用到bus_type中的match函数,进行比较查找,如果找到对应的驱动或则设备,那么就会调用到driver中的probe函数,所以我们应该在probe函数中进行创建设备驱动和对应的初始化。

platform_driver(下面代码例程为了省事直接复制正点原子中的部分)

/* 设备结构体 */
struct xxx_dev
{
    struct cdev cdev;
    /* 设备结构体其他具体内容 */
};

struct xxx_dev xxxdev; /* 定义个设备结构体变量 */

static int xxx_open(struct inode *inode, struct file *filp)
{
    /* 函数具体内容 */
    return 0;
}

static ssize_t xxx_write(struct file *filp, const char __user *buf,
                         ze_t cnt, loff_t *offt)
{
    /* 函数具体内容 */
    return 0;
}

/*
 * 字符设备驱动操作集
 */
static struct file_operations xxx_fops = {
    .owner = THIS_MODULE,
    .open = xxx_open,
    .write = xxx_write,
};

/*
 * platform 驱动的 probe 函数
 * 驱动与设备匹配成功以后此函数就会执行
 */
static int xxx_probe(struct platform_device *dev)
{
    ...... cdev_init(&xxxdev.cdev, &xxx_fops); /* 注册字符设备驱动 */
    /* 函数具体内容 */
    return 0;
}

static int xxx_remove(struct platform_device *dev)
{
    ...... cdev_del(&xxxdev.cdev); /* 删除 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 xxxdriver_init(void)
{
    return platform_driver_register(&xxx_driver);
}

/* 驱动模块卸载 */
static void __exit xxxdriver_exit(void)
{
    platform_driver_unregister(&xxx_driver);
}

module_init(xxxdriver_init);
module_exit(xxxdriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("you_name");

platform_device(是在不在设备树下面描述设备信息的时候,通过device去描述设备,一般情况下我们都是会用设备树

其中name 表示设备名字,要和所使用的 platform_driver驱动的 name 字段相同,否则的话设
备就无法匹配到对应的驱动。

num_resources 表示资源数量,一般为第 28 行 resource 资源的大小。

resource 表示资源,也就是设备信息,比如外设寄存器等。

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;
};

device_driver(直接复制正点原子的教程中的部分资料)

/* 寄存器地址定义*/
#define PERIPH1_REGISTER_BASE (0X20000000) /* 外设 1 寄存器首地址 */
#define PERIPH2_REGISTER_BASE (0X020E0068) /* 外设 2 寄存器首地址 */
#define REGISTER_LENGTH 4

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

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

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

/* 设备模块注销 */
static void __exit xxx_resourcesdevice_exit(void)
{
    platform_device_unregister(&xxxdevice);
}

module_init(xxxdevice_init);
module_exit(xxxdevice_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yourname");

device利用设备树去描述

此时不需要使用device_driver。在设备树中compatible属性去描述,通过该属性去与platform进行匹配。

//设备树添加设备节点
1 gpioled {
2 #address-cells = <1>;
3 #size-cells = <1>;
4 compatible = "my-gpioled";
5 pinctrl-names = "default";
6 pinctrl-0 = <&pinctrl_led>;
7 led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
8 status = "okay";
9 };

//platform中定义设备匹配信息
1 static const struct of_device_id leds_of_match[] = {
2 { .compatible = "my-gpioled" }, /*  兼容属性 */
3 { /* Sentinel */ }
4 };
5
6 MODULE_DEVICE_TABLE(of, leds_of_match);
7
8 static struct platform_driver leds_platform_driver = {
9  .driver = {
10   .name = "imx6ul-led",
11   .of_match_table = leds_of_match,
12 },
13 .probe = leds_probe,
14 .remove = leds_remove,
15 };

你可能感兴趣的:(linux学习笔记,linux,嵌入式)